1/*
2 * Copyright (c) 1998-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/types.h>                       // (miscfs/devfs/devfs.h, ...)
25
26#include <miscfs/devfs/devfs.h>              // (devfs_make_node, ...)
27#include <sys/buf.h>                         // (buf_t, ...)
28#include <sys/fcntl.h>                       // (FWRITE, ...)
29#include <sys/ioccom.h>                      // (IOCGROUP, ...)
30#include <sys/proc.h>                        // (proc_is64bit, ...)
31#include <sys/stat.h>                        // (S_ISBLK, ...)
32#include <sys/systm.h>                       // (DEV_BSIZE, ...)
33#include <IOKit/assert.h>
34#include <IOKit/IOBSD.h>
35#include <IOKit/IODeviceTreeSupport.h>
36#include <IOKit/IOLib.h>
37#include <IOKit/IOKitKeys.h>
38#include <IOKit/IOMemoryDescriptor.h>
39#include <IOKit/IOMessage.h>
40#include <IOKit/IOSubMemoryDescriptor.h>
41#include <IOKit/storage/IOBlockStorageDevice.h>
42#include <IOKit/storage/IOBlockStorageDriver.h>
43#include <IOKit/storage/IOMedia.h>
44#include <IOKit/storage/IOMediaBSDClient.h>
45///w:start
46#if !TARGET_OS_EMBEDDED
47#include <IOKit/pwr_mgt/RootDomain.h>
48#endif /* !TARGET_OS_EMBEDDED */
49///w:stop
50
51#define super IOService
52OSDefineMetaClassAndStructors(IOMediaBSDClient, IOService)
53
54const UInt32 kMinorsAddCountBits = 6;
55const UInt32 kMinorsAddCountMask = (1 << kMinorsAddCountBits) - 1;
56const UInt32 kMinorsAddCount     = (1 << kMinorsAddCountBits);
57const UInt32 kMinorsMaxCountBits = 16;
58const UInt32 kMinorsMaxCountMask = (1 << kMinorsMaxCountBits) - 1;
59const UInt32 kMinorsMaxCount     = (1 << kMinorsMaxCountBits);
60const UInt32 kMinorsBucketCount  = kMinorsMaxCount / kMinorsAddCount;
61const UInt32 kAnchorsAddCount    = 2;
62const UInt32 kAnchorsMaxCount    = kMinorsMaxCount;
63
64#define kMsgNoWhole    "%s: No whole media found for media \"%s\".\n", getName()
65#define kMsgNoLocation "%s: No location is found for media \"%s\".\n", getName()
66
67extern "C"
68{
69    int  dkclose(dev_t dev, int flags, int devtype, proc_t proc);
70    int  dkioctl(dev_t dev, u_long cmd, caddr_t data, int flags, proc_t proc);
71    int  dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int flags, proc_t proc);
72    int  dkioctl_cdev(dev_t dev, u_long cmd, caddr_t data, int flags, proc_t proc);
73    int  dkopen(dev_t dev, int flags, int devtype, proc_t proc);
74    int  dkread(dev_t dev, uio_t uio, int flags);
75    int  dksize(dev_t dev);
76    void dkstrategy(buf_t bp);
77    int  dkwrite(dev_t dev, uio_t uio, int flags);
78} // extern "C"
79
80static struct bdevsw bdevswFunctions =
81{
82    /* d_open     */ dkopen,
83    /* d_close    */ dkclose,
84    /* d_strategy */ dkstrategy,
85    /* d_ioctl    */ dkioctl_bdev,
86    /* d_dump     */ eno_dump,
87    /* d_psize    */ dksize,
88    /* d_type     */ D_DISK
89};
90
91struct cdevsw cdevswFunctions =
92{
93    /* d_open     */ dkopen,
94    /* d_close    */ dkclose,
95    /* d_read     */ dkread,
96    /* d_write    */ dkwrite,
97    /* d_ioctl    */ dkioctl_cdev,
98    /* d_stop     */ eno_stop,
99    /* d_reset    */ eno_reset,
100    /* d_ttys     */ 0,
101    /* d_select   */ eno_select,
102    /* d_mmap     */ eno_mmap,
103    /* d_strategy */ eno_strat,
104    /* d_getc     */ eno_getc,
105    /* d_putc     */ eno_putc,
106    /* d_type     */ D_DISK
107};
108
109struct dio { dev_t dev; uio_t uio; void * drvdata; };
110
111typedef struct dio *                      dio_t;
112typedef void *                            dkr_t;       /* dkreadwrite request */
113typedef enum { DKRTYPE_BUF, DKRTYPE_DIO } dkrtype_t;
114
115static int  dkreadwrite(dkr_t dkr, dkrtype_t dkrtype);
116static void dkreadwritecompletion(void *, void *, IOReturn, UInt64);
117
118inline int32_t getminor(dev_t dev)
119{
120    return minor(dev);
121}
122
123const UInt32 kInvalidAnchorID = (UInt32) (-1);
124
125struct AnchorSlot
126{
127    UInt32       isAssigned:1; // (slot is occupied)
128    UInt32       isObsolete:1; // (slot is to be removed once references gone)
129
130    IOService *  anchor;       // (anchor object)
131    void *       key;          // (anchor key)
132    IONotifier * notifier;     // (anchor termination notification, post-stop)
133};
134
135class AnchorTable
136{
137protected:
138    AnchorSlot * _table;
139    UInt32       _tableCount;
140
141    static IOReturn anchorWasNotified( void *      target,
142                                       void *      parameter,
143                                       UInt32      messageType,
144                                       IOService * provider,
145                                       void *      messageArgument,
146                                       vm_size_t   messageArgumentSize );
147
148public:
149    AnchorTable();
150    ~AnchorTable();
151
152    UInt32 insert(IOService * anchor, void * key);
153    UInt32 locate(IOService * anchor);
154    UInt32 locate(IOService * anchor, void * key);
155    void   obsolete(UInt32 anchorID);
156    void   remove(UInt32 anchorID);
157    UInt32 update(IOService * anchor, void * key);
158
159    bool   isObsolete(UInt32 anchorID);
160};
161
162const UInt32 kInvalidMinorID = (UInt32) (-1);
163
164struct MinorSlot
165{
166    UInt32             isAssigned:1;  // (slot is occupied)
167    UInt32             isObsolete:1;  // (slot is to be removed, close pending)
168    UInt32             isOrphaned:1;  // (slot is in  open flux, close pending)
169
170    UInt32             anchorID;      // (minor's associated anchor ID)
171    IOMediaBSDClient * client;        // (minor's media bsd client object)
172    IOMedia *          media;         // (minor's media object)
173    char *             name;          // (minor's name, private allocation)
174
175    UInt64             bdevBlockSize; // (block device's preferred block size)
176    void *             bdevNode;      // (block device's devfs node)
177    UInt32             bdevOpen;      // (block device's open count)
178    IOStorageAccess    bdevOpenLevel; // (block device's open level)
179
180    void *             cdevNode;      // (character device's devfs node)
181    UInt32             cdevOpen;      // (character device's open count)
182    IOStorageAccess    cdevOpenLevel; // (character device's open level)
183#if TARGET_OS_EMBEDDED
184    IOStorageOptions   cdevOptions;
185#endif /* TARGET_OS_EMBEDDED */
186};
187
188class MinorTable
189{
190protected:
191    class
192    {
193    public:
194        MinorSlot ** buckets;
195
196        inline MinorSlot & operator[](const int i)
197        {
198            return (buckets[i >> kMinorsAddCountBits])[i & kMinorsAddCountMask];
199        }
200    } _table;
201
202    UInt32 _tableCount;
203
204public:
205    MinorTable();
206    ~MinorTable();
207
208    UInt32      insert( IOMedia *          media,
209                        UInt32             anchorID,
210                        IOMediaBSDClient * client,
211                        char *             slicePath );
212
213    UInt32      update( IOMedia *          media,
214                        UInt32             anchorID,
215                        IOMediaBSDClient * client,
216                        char *             slicePath );
217
218    UInt32      locate(IOMedia * media);
219    void        obsolete(UInt32 minorID);
220    void        remove(UInt32 minorID);
221
222    bool        isObsolete(UInt32 minorID);
223
224    MinorSlot * getMinor(UInt32 minorID);
225
226    UInt32      getOpenCountForAnchorID(UInt32 anchorID);
227    bool        hasReferencesToAnchorID(UInt32 anchorID, bool excludeOrphans);
228};
229
230const UInt32 kInvalidMajorID = (UInt32) (-1);
231
232class IOMediaBSDClientGlobals
233{
234protected:
235    AnchorTable * _anchors;           // (table of anchors)
236    MinorTable *  _minors;            // (table of minors)
237
238    UInt32        _majorID;           // (major ID)
239
240    IOLock *      _openLock;          // (lock for opens, closes)
241    IOLock *      _stateLock;         // (lock for state, tables)
242///w:start
243#if !TARGET_OS_EMBEDDED
244    thread_call_t         _assertionCall;
245    IOPMDriverAssertionID _assertionID;
246    IOLock *              _assertionLock;
247    AbsoluteTime          _assertionTime;
248#endif /* !TARGET_OS_EMBEDDED */
249///w:stop
250
251public:
252    IOMediaBSDClientGlobals();
253    ~IOMediaBSDClientGlobals();
254
255    AnchorTable * getAnchors();
256    MinorTable *  getMinors();
257    MinorSlot *   getMinor(UInt32 minorID);
258
259    UInt32        getMajorID();
260
261    bool          isValid();
262
263    void          lockOpen();
264    void          unlockOpen();
265
266    void          lockState();
267    void          unlockState();
268///w:start
269#if !TARGET_OS_EMBEDDED
270    thread_call_t         getAssertionCall();
271
272    IOPMDriverAssertionID getAssertionID();
273    void                  setAssertionID(IOPMDriverAssertionID assertionID);
274
275    AbsoluteTime          getAssertionTime();
276    void                  setAssertionTime(AbsoluteTime assertionTime);
277
278    void                  lockAssertion();
279    void                  unlockAssertion();
280#endif /* !TARGET_OS_EMBEDDED */
281///w:stop
282};
283
284static IOMediaBSDClientGlobals gIOMediaBSDClientGlobals;
285
286bool IOMediaBSDClient::init(OSDictionary * properties)
287{
288    //
289    // Initialize this object's minimal state.
290    //
291
292    // Ask our superclass' opinion.
293
294    if ( super::init(properties) == false )  return false;
295
296    // Determine whether our minimal global state has been initialized.
297
298    if ( gIOMediaBSDClientGlobals.isValid() == false )  return false;
299
300    // Initialize this object's minimal state.
301
302    _anchors = gIOMediaBSDClientGlobals.getAnchors();
303    _minors  = gIOMediaBSDClientGlobals.getMinors();
304
305    return true;
306}
307
308void IOMediaBSDClient::free()
309{
310    //
311    // Free all of this object's outstanding resources.
312    //
313
314    super::free();
315}
316
317bool IOMediaBSDClient::start(IOService * provider)
318{
319    //
320    // This method is called once we have been attached to the provider object.
321    //
322
323    IOMedia * media = (IOMedia *) provider;
324
325    // Ask our superclass' opinion.
326
327    if ( super::start(provider) == false )  return false;
328
329    // Disable access to tables.
330
331    gIOMediaBSDClientGlobals.lockState();
332
333    // Create bdevsw and cdevsw nodes for the new media object.
334
335    createNodes(media);
336
337    // Enable access to tables.
338
339    gIOMediaBSDClientGlobals.unlockState();
340
341    // Register this object so it can be found via notification requests. It is
342    // not being registered to have I/O Kit attempt to have drivers match on it,
343    // which is the reason most other services are registered -- that's not the
344    // intention of this registerService call.
345
346    registerService();
347
348    return true;
349}
350
351bool IOMediaBSDClient::terminate(IOOptionBits options)
352{
353    //
354    // This method is called when we are to terminate from the provider object.
355    //
356
357    UInt32 minorID;
358
359    // Ask our superclass' opinion.
360
361    if ( super::terminate(options) == false )  return false;
362
363    // Disable access to tables.
364
365    gIOMediaBSDClientGlobals.lockState();
366
367    // Find the minor assigned to this media.
368
369    minorID = gIOMediaBSDClientGlobals.getMinors()->locate(getProvider());
370
371    if ( minorID != kInvalidMinorID )
372    {
373        MinorSlot * minor;
374
375        minor = gIOMediaBSDClientGlobals.getMinors()->getMinor(minorID);
376
377        // Remove the minor from the minor table.  If an open is still
378        // outstanding, we mark the minor as obsolete for removal when
379        // the close comes in later.
380
381        if ( minor->bdevOpen || minor->cdevOpen )
382        {
383            gIOMediaBSDClientGlobals.getMinors()->obsolete(minorID);
384        }
385        else
386        {
387            gIOMediaBSDClientGlobals.getMinors()->remove(minorID);
388        }
389    }
390
391    // Enable access to tables.
392
393    gIOMediaBSDClientGlobals.unlockState();
394
395    return true;
396}
397
398IOMedia * IOMediaBSDClient::getWholeMedia( IOMedia * media,
399                                           UInt32 *  slicePathSize,
400                                           char *    slicePath )
401{
402    //
403    // Find the whole media that roots this media tree.  A null return value
404    // indicates no whole media was found or a malformed tree was detected.
405    //
406    // If slicePathSize is non-zero, the size required to fit the slice path
407    // (including the zero terminator) is passed back as a result.
408    //
409    // If slicePathSize and slicePath are both non-zero, the slice path will
410    // be written into the slicePath buffer.  The value slicePathSize points
411    // to must be the size of the slicePath buffer, which is used for sanity
412    // checking in this method.
413    //
414    // This method assumes that the table (and termination) lock is held.
415    //
416
417    UInt32      depth    = 1;
418    UInt32      position = sizeof('\0');
419    IOService * service  = 0;
420
421    assert(slicePath == 0 || slicePathSize != 0);
422
423    // Search the registry for the parent whole media for this media.
424
425    for ( service = media; service; service = service->getProvider() )
426    {
427        if ( OSDynamicCast(IOMedia, service) )               // (is it a media?)
428        {
429            if ( ((IOMedia *)service)->isWhole() )     // (is it a whole media?)
430            {
431                if ( slicePath )            // (are we building the slice path?)
432                {
433                    slicePath[*slicePathSize - 1] = 0;  // (zero terminate path)
434
435                    if ( position < *slicePathSize )     // (need to move path?)
436                    {
437                        memmove( slicePath,    // (move path to start of buffer)
438                                 slicePath + (*slicePathSize - position),
439                                 position );
440                    }
441                }
442                else if ( slicePathSize ) // (report size req'd for slice path?)
443                {
444                    *slicePathSize = position;
445                }
446
447                return (IOMedia *)service;           // (return the whole media)
448            }
449
450            // Determine whether this non-whole media has a location value.  It
451            // must, by definition of a non-whole media, but if it does not, we
452            // should return an error condition.
453
454            const char * location = service->getLocation();
455
456            if ( location == 0 )            // (no location on non-whole media?)
457            {
458                if ( service == media ) IOLog(kMsgNoLocation, media->getName());
459                return 0;
460            }
461
462            // Otherwise, it's a valid non-whole media: we compute the required
463            // size for the slice path or build the slice path, if so requested.
464            // Note that the slice path is built backwards from the ends of the
465            // supplied buffer to the beginning of the buffer.
466
467            position += sizeof('s') + strlen(location);
468
469            if ( slicePath )                          // (build the slice path?)
470            {
471                char * path = slicePath + *slicePathSize - position;
472
473                if ( position > *slicePathSize )  { assert(0);  return 0; }
474
475                *path = 's';
476                strncpy(path + sizeof('s'), location, strlen(location));
477            }
478
479            depth += 1;
480        }
481    }
482
483    // If we've fallen through, then the whole media was never found.
484
485    if ( depth == 1 )  IOLog(kMsgNoWhole, media->getName());
486
487    return 0;
488}
489
490bool IOMediaBSDClient::createNodes(IOMedia * media)
491{
492    //
493    // Create bdevsw and cdevsw nodes for the given media object.
494    //
495    // This method assumes that the table (and termination) lock is held.
496    //
497
498    IOService *   anchor;
499    AnchorTable * anchors   = gIOMediaBSDClientGlobals.getAnchors();
500    UInt32        anchorID;
501    bool          anchorNew = false;
502    UInt32        majorID   = gIOMediaBSDClientGlobals.getMajorID();
503    MinorTable *  minors    = gIOMediaBSDClientGlobals.getMinors();
504    UInt32        minorID;
505    char *        slicePath = 0;
506    UInt32        slicePathSize;
507    IOMedia *     whole;
508
509    //
510    // Find the anchor that roots this media tree.  The anchor is defined as the
511    // parent of the whole media that roots this media tree.  It is an important
512    // object to us because this object stays in place when media is ejected, so
513    // we can continue to maintain the "unit number" of the "drive" such that if
514    // media is re-inserted, it will show up under the same "unit number".   You
515    // can think of the typical anchor as being the drive, if it helps, although
516    // it could be one of many other kinds of drivers (eg. a RAID scheme).
517    //
518
519    whole = getWholeMedia(media, &slicePathSize);
520    if ( whole == 0 )  return false;
521
522    anchor = whole->getProvider();
523    if ( anchor == 0 )  return false;
524
525    //
526    // Determine whether the anchor already exists in the anchor table (obsolete
527    // occurences are skipped in the search, as appropriate,  since those anchor
528    // IDs are to be removed soon). If the anchor does not exist, insert it into
529    // anchor table.
530    //
531
532    anchorID = anchors->locate(anchor, whole);
533
534    if ( anchorID == kInvalidAnchorID )
535    {
536        //
537        // The anchor and key pair does not exist in the table, however we still
538        // have more to check.  The anchor might in fact exist in the table, but
539        // have a different key.  If such a slot exists, and it isn't referenced
540        // in the minor table, we reuse the slot.
541        //
542
543        anchorID = anchors->update(anchor, whole);
544    }
545
546    if ( anchorID == kInvalidAnchorID )
547    {
548        anchorID = anchors->insert(anchor, whole);        // (get new anchor ID)
549        if ( anchorID == kInvalidAnchorID )  return false;
550        anchorNew = true;
551    }
552
553    //
554    // Allocate space for and build the slice path for the device node names.
555    //
556
557    slicePath = (char *) IOMalloc(slicePathSize);
558    if ( slicePath == 0 )  goto createNodesErr;
559
560    whole = getWholeMedia(media, &slicePathSize, slicePath);
561    assert(whole);
562
563    //
564    // Insert the new media into our minor table (we're almost done :-).
565    //
566
567    minorID = minors->update(media, anchorID, this, slicePath);
568
569    if ( minorID == kInvalidMinorID )
570    {
571        minorID = minors->insert(media, anchorID, this, slicePath);
572        if ( minorID == kInvalidMinorID )  goto createNodesErr;
573    }
574
575    //
576    // Create the required properties on the media.
577    //
578
579    media->setProperty(kIOBSDNameKey,  minors->getMinor(minorID)->name);
580    media->setProperty(kIOBSDUnitKey,  anchorID, 32);           // ("BSD Unit" )
581    media->setProperty(kIOBSDMajorKey, majorID,  32);           // ("BSD Major")
582    media->setProperty(kIOBSDMinorKey, minorID,  32);           // ("BSD Minor")
583
584    //
585    // Clean up outstanding resources.
586    //
587
588    IOFree(slicePath, slicePathSize);
589
590    return true; // (success)
591
592createNodesErr:
593
594    if (anchorNew)  anchors->remove(anchorID);
595    if (slicePath)  IOFree(slicePath, slicePathSize);
596
597    return false; // (failure)
598}
599
600#ifndef __LP64__
601AnchorTable * IOMediaBSDClient::getAnchors()
602{
603    //
604    // Obtain the table of anchors.
605    //
606
607    return _anchors;
608}
609
610MinorTable * IOMediaBSDClient::getMinors()
611{
612    //
613    // Obtain the table of anchors.
614    //
615
616    return _minors;
617}
618
619MinorSlot * IOMediaBSDClient::getMinor(UInt32 minorID)
620{
621    //
622    // Obtain information for the specified minor ID.
623    //
624
625    return _minors->getMinor(minorID);
626}
627#endif /* !__LP64__ */
628
629IOMedia * IOMediaBSDClient::getProvider() const
630{
631    //
632    // Obtain this object's provider.  We override the superclass's method to
633    // return a more specific subclass of IOService -- IOMedia.   This method
634    // serves simply as a convenience to subclass developers.
635    //
636
637    return (IOMedia *) IOService::getProvider();
638}
639
640int IOMediaBSDClient::ioctl( dev_t   dev,
641                             u_long  cmd,
642                             caddr_t data,
643                             int     flags,
644                             proc_t  proc )
645{
646    //
647    // Process a foreign ioctl.
648    //
649
650    return ENOTTY;
651}
652
653#ifdef __LP64__
654OSMetaClassDefineReservedUnused(IOMediaBSDClient,  0);
655#else /* !__LP64__ */
656OSMetaClassDefineReservedUsed(IOMediaBSDClient,  0);
657#endif /* !__LP64__ */
658OSMetaClassDefineReservedUnused(IOMediaBSDClient,  1);
659OSMetaClassDefineReservedUnused(IOMediaBSDClient,  2);
660OSMetaClassDefineReservedUnused(IOMediaBSDClient,  3);
661OSMetaClassDefineReservedUnused(IOMediaBSDClient,  4);
662OSMetaClassDefineReservedUnused(IOMediaBSDClient,  5);
663OSMetaClassDefineReservedUnused(IOMediaBSDClient,  6);
664OSMetaClassDefineReservedUnused(IOMediaBSDClient,  7);
665OSMetaClassDefineReservedUnused(IOMediaBSDClient,  8);
666OSMetaClassDefineReservedUnused(IOMediaBSDClient,  9);
667OSMetaClassDefineReservedUnused(IOMediaBSDClient, 10);
668OSMetaClassDefineReservedUnused(IOMediaBSDClient, 11);
669OSMetaClassDefineReservedUnused(IOMediaBSDClient, 12);
670OSMetaClassDefineReservedUnused(IOMediaBSDClient, 13);
671OSMetaClassDefineReservedUnused(IOMediaBSDClient, 14);
672OSMetaClassDefineReservedUnused(IOMediaBSDClient, 15);
673
674// =============================================================================
675// BSD Functions
676
677typedef struct
678{
679    user32_addr_t capacities;
680    uint32_t      capacitiesCount;
681
682    uint8_t       reserved0064[8];
683} dk_format_capacities_32_t;
684
685typedef struct
686{
687    user64_addr_t capacities;
688    uint32_t      capacitiesCount;
689
690    uint8_t       reserved0096[4];
691} dk_format_capacities_64_t;
692
693typedef struct
694{
695    user32_addr_t extents;
696    uint32_t      extentsCount;
697
698    uint32_t      options;
699
700    uint8_t       reserved0096[4];
701} dk_unmap_32_t;
702
703typedef struct
704{
705    user64_addr_t extents;
706    uint32_t      extentsCount;
707
708    uint32_t      options;
709} dk_unmap_64_t;
710
711typedef struct
712{
713    user32_addr_t extents;
714    uint32_t      extentsCount;
715
716    uint8_t       tier;
717
718    uint8_t       reserved0072[7];
719} dk_set_tier_32_t;
720
721typedef struct
722{
723    user64_addr_t extents;
724    uint32_t      extentsCount;
725
726    uint8_t       tier;
727
728    uint8_t       reserved0104[3];
729} dk_set_tier_64_t;
730
731static IOStorageAccess DK_ADD_ACCESS(IOStorageAccess a1, IOStorageAccess a2)
732{
733    static UInt8 table[4][4] =
734    {            /* Rea, Wri, R|S, W|S */
735        /* Rea */ { 000, 001, 002, 003 },
736        /* Wri */ { 001, 001, 001, 001 },
737        /* R|S */ { 002, 001, 002, 003 },
738        /* W|S */ { 003, 001, 003, 003 }
739    };
740
741    if ( a1 == kIOStorageAccessNone )  return a2;
742    if ( a2 == kIOStorageAccessNone )  return a1;
743
744    a1 = (a1 - 1) >> 1;
745    a2 = (a2 - 1) >> 1;
746
747    if ( a1 > 003 )  return kIOStorageAccessNone;
748    if ( a2 > 003 )  return kIOStorageAccessNone;
749
750    return (table[a1][a2] << 1) + 1;
751}
752
753static bool DKIOC_IS_RESERVED(caddr_t data, uint32_t reserved)
754{
755    UInt32 index;
756
757    for ( index = 0; index < sizeof(reserved) * 8; index++, reserved >>= 1 )
758    {
759        if ( (reserved & 1) )
760        {
761            if ( data[index] )  return true;
762        }
763    }
764
765    return false;
766}
767
768UInt64 _IOMediaBSDClientGetThrottleMask(IOMedia * media)
769{
770    UInt64 mask;
771
772    mask = 0;
773
774    if ( media )
775    {
776        int error;
777
778        error = EAGAIN;
779
780        while ( error )
781        {
782            // Iterate through IOBlockStorageDevice objects.
783
784            IORegistryIterator * devices;
785
786            error = 0;
787
788            mask = 0;
789
790            devices = IORegistryIterator::iterateOver( media, gIOServicePlane, kIORegistryIterateParents );
791
792            if ( devices )
793            {
794                IORegistryEntry * device;
795
796                device = devices->getNextObjectRecursive( );
797
798                while ( device )
799                {
800                    if ( OSDynamicCast( IOBlockStorageDevice, device ) )
801                    {
802                        // Iterate through IOMedia objects.
803
804                        IORegistryIterator * services;
805
806                        services = IORegistryIterator::iterateOver( device, gIOServicePlane );
807
808                        if ( services )
809                        {
810                            IORegistryEntry * service;
811
812                            service = services->getNextObjectRecursive( );
813
814                            while ( service )
815                            {
816                                if ( OSDynamicCast( IOMedia, service ) )
817                                {
818                                    // Obtain the BSD Unit property.
819
820                                    OSNumber * unit;
821
822                                    unit = OSDynamicCast( OSNumber, service->getProperty( kIOBSDUnitKey ) );
823
824                                    if ( unit )
825                                    {
826                                        mask |= 1 << ( unit->unsigned32BitValue( ) % 64 );
827                                    }
828                                }
829
830                                service = services->getNextObjectRecursive( );
831                            }
832
833                            if ( services->isValid( ) == false )
834                            {
835                                error = EAGAIN;
836                            }
837
838                            services->release( );
839                        }
840
841///w:start
842                        OSNumber * number;
843
844                        number = OSDynamicCast( OSNumber, device->getProperty( "throttle-unit" ) );
845
846                        if ( number )
847                        {
848                            OSDictionary * dictionary;
849
850                            dictionary = IOService::serviceMatching( kIOMediaClass );
851
852                            if ( dictionary )
853                            {
854                                OSIterator * iterator;
855
856                                dictionary->setObject( kIOBSDUnitKey, number );
857
858                                iterator = IOService::getMatchingServices( dictionary );
859
860                                if ( iterator )
861                                {
862                                    OSObject * object;
863
864                                    object = iterator->getNextObject( );
865
866                                    if ( object )
867                                    {
868                                        mask |= _IOMediaBSDClientGetThrottleMask( ( IOMedia * ) object );
869                                    }
870
871                                    iterator->release( );
872                                }
873
874                                dictionary->release( );
875                            }
876                        }
877///w:stop
878                        devices->exitEntry( );
879                    }
880
881                    device = devices->getNextObjectRecursive( );
882                }
883
884                if ( devices->isValid( ) == false )
885                {
886                    error = EAGAIN;
887                }
888
889                devices->release( );
890            }
891        }
892    }
893
894    return mask;
895}
896
897int dkopen(dev_t dev, int flags, int devtype, proc_t /* proc */)
898{
899    //
900    // dkopen opens the device (called on each open).
901    //
902
903    IOStorageAccess access;
904    int             error;
905    IOStorageAccess level;
906    IOStorageAccess levelOut;
907    IOMedia *       media;
908    MinorSlot *     minor;
909
910    assert(S_ISBLK(devtype) || S_ISCHR(devtype));
911
912    gIOMediaBSDClientGlobals.lockOpen();    // (disable access to opens, closes)
913    gIOMediaBSDClientGlobals.lockState();   // (disable access to state, tables)
914
915    access  = kIOStorageAccessReader;
916    access |= (flags &   FWRITE) ? kIOStorageAccessReaderWriter  : 0;
917    access |= (flags & O_SHLOCK) ? kIOStorageAccessSharedLock    : 0;
918    access |= (flags & O_EXLOCK) ? kIOStorageAccessExclusiveLock : 0;
919
920    error = 0;
921    media = 0;
922    minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
923
924    //
925    // Process the open.
926    //
927
928    if ( minor == 0 )                                       // (is minor valid?)
929    {
930        error = ENXIO;
931    }
932    else if ( minor->isOrphaned )                         // (is minor in flux?)
933    {
934        error = EBUSY;
935    }
936    else
937    {
938///w:start
939#ifdef __LP64__
940        static int root = 0;
941
942        if ( root == 0 )
943        {
944            root = 1;
945
946            if ( minor->media->isWritable() )
947            {
948                access |= kIOStorageAccessReaderWriter;
949            }
950        }
951#endif /* __LP64__ */
952///w:stop
953        level    = DK_ADD_ACCESS(minor->bdevOpenLevel, minor->cdevOpenLevel);
954        levelOut = DK_ADD_ACCESS(level, access);
955
956        if ( levelOut == kIOStorageAccessNone )            // (is access valid?)
957        {
958            error = EBUSY;
959        }
960        else if ( (flags & FWRITE) )                    // (is client a writer?)
961        {
962            if ( minor->media->isWritable() == false )
963            {
964                error = EACCES;
965            }
966        }
967    }
968
969    if ( error == 0 )                                                   // (go?)
970    {
971        IOStorageAccess wasOpenLevel;
972
973        if ( S_ISBLK(devtype) )                                // (update state)
974        {
975            minor->bdevOpen++;
976            wasOpenLevel = minor->bdevOpenLevel;
977            minor->bdevOpenLevel = DK_ADD_ACCESS(wasOpenLevel, access);
978        }
979        else
980        {
981            minor->cdevOpen++;
982            wasOpenLevel = minor->cdevOpenLevel;
983            minor->cdevOpenLevel = DK_ADD_ACCESS(wasOpenLevel, access);
984        }
985
986        gIOMediaBSDClientGlobals.unlockState();     // (enable access to tables)
987
988        if ( level != levelOut )                        // (issue open/upgrade?)
989        {
990            bool success;
991
992            media = minor->media;
993            minor->media->retain();
994
995            success = minor->media->open(minor->client, 0, levelOut);    // (go)
996
997            if ( success == false )
998            {
999                gIOMediaBSDClientGlobals.lockState();        // (disable access)
1000
1001                if ( S_ISBLK(devtype) )                          // (undo state)
1002                {
1003                    minor->bdevOpen--;
1004                    minor->bdevOpenLevel = wasOpenLevel;
1005                }
1006                else
1007                {
1008                    minor->cdevOpen--;
1009                    minor->cdevOpenLevel = wasOpenLevel;
1010                }
1011
1012                assert(minor->isOrphaned == false);
1013
1014                if ( !minor->bdevOpen && !minor->cdevOpen && minor->isObsolete )
1015                {
1016                    gIOMediaBSDClientGlobals.getMinors()->remove(getminor(dev));
1017                }
1018
1019                gIOMediaBSDClientGlobals.unlockState();       // (enable access)
1020
1021                error = EBUSY;
1022            }
1023        }
1024    }
1025    else
1026    {
1027        gIOMediaBSDClientGlobals.unlockState();     // (enable access to tables)
1028    }
1029
1030    gIOMediaBSDClientGlobals.unlockOpen();   // (enable access to opens, closes)
1031
1032    //
1033    // Wait until I/O Kit has finished to attempt to match storage drivers
1034    // or terminate storage drivers, should the media object have been re-
1035    // registered or its storage driver been terminated as a result of the
1036    // open.
1037    //
1038
1039    if ( media )
1040    {
1041        media->waitQuiet();
1042        media->release();
1043    }
1044
1045    return error;
1046}
1047
1048int dkclose(dev_t dev, int /* flags */, int devtype, proc_t /* proc */)
1049{
1050    //
1051    // dkclose closes the device (called on last close).
1052    //
1053
1054    IOStorageAccess level;
1055    IOStorageAccess levelOut;
1056    IOMedia *       media;
1057    MinorSlot *     minor;
1058
1059    assert(S_ISBLK(devtype) || S_ISCHR(devtype));
1060
1061    gIOMediaBSDClientGlobals.lockOpen();    // (disable access to opens, closes)
1062    gIOMediaBSDClientGlobals.lockState();   // (disable access to state, tables)
1063
1064    media = 0;
1065    minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
1066
1067    level = DK_ADD_ACCESS(minor->bdevOpenLevel, minor->cdevOpenLevel);
1068
1069    if ( S_ISBLK(devtype) )                                    // (update state)
1070    {
1071        minor->bdevBlockSize = minor->media->getPreferredBlockSize();
1072        minor->bdevOpen      = 0;
1073        minor->bdevOpenLevel = kIOStorageAccessNone;
1074    }
1075    else
1076    {
1077        minor->cdevOpen      = 0;
1078        minor->cdevOpenLevel = kIOStorageAccessNone;
1079#if TARGET_OS_EMBEDDED
1080        minor->cdevOptions   = 0;
1081#endif /* TARGET_OS_EMBEDDED */
1082    }
1083
1084    levelOut = DK_ADD_ACCESS(minor->bdevOpenLevel, minor->cdevOpenLevel);
1085
1086    if ( minor->isOrphaned )                              // (is minor in flux?)
1087    {
1088        //
1089        // We have determined that the specified minor is in "open flux".  This
1090        // means we are in a state where the media object has been closed, only
1091        // the device node is still open.  This happens to the minor subsequent
1092        // to a DKIOCEJECT ioctl -- this close resets the flux state to normal.
1093        //
1094
1095        minor->isOrphaned = false;
1096
1097        // If this minor is marked as obsolete, then we've already received the
1098        // media's termination notification, but the minor is yet to be removed
1099        // from the table -- remove it now.
1100
1101        assert(minor->bdevOpen == 0);
1102        assert(minor->cdevOpen == 0);
1103
1104        if ( minor->isObsolete )
1105        {
1106            gIOMediaBSDClientGlobals.getMinors()->remove(getminor(dev));
1107        }
1108
1109        gIOMediaBSDClientGlobals.unlockState();     // (enable access to tables)
1110    }
1111    else if ( !minor->bdevOpen && !minor->cdevOpen )
1112    {
1113        //
1114        // We communicate the close down to the media object once all opens are
1115        // gone, on both the block and character device nodes.
1116        //
1117
1118        IOMediaBSDClient * client;
1119
1120        client = minor->client;
1121        minor->client->retain();
1122
1123        media = minor->media;
1124        minor->media->retain();
1125
1126        // If this minor is marked as obsolete, then we've already received the
1127        // media's termination notification, but the minor is yet to be removed
1128        // from the table -- remove it now.
1129
1130        if ( minor->isObsolete )
1131        {
1132            gIOMediaBSDClientGlobals.getMinors()->remove(getminor(dev));
1133        }
1134
1135        gIOMediaBSDClientGlobals.unlockState();     // (enable access to tables)
1136
1137        media->close(client);                                            // (go)
1138
1139        client->release();
1140    }
1141    else if ( level != levelOut )
1142    {
1143        //
1144        // We communicate the downgrade down to the media object.
1145        //
1146
1147        media = minor->media;
1148        minor->media->retain();
1149
1150        gIOMediaBSDClientGlobals.unlockState();     // (enable access to tables)
1151
1152        minor->media->open(minor->client, 0, levelOut);                  // (go)
1153    }
1154    else
1155    {
1156        gIOMediaBSDClientGlobals.unlockState();     // (enable access to tables)
1157    }
1158
1159    gIOMediaBSDClientGlobals.unlockOpen();   // (enable access to opens, closes)
1160
1161    //
1162    // Wait until I/O Kit has finished to attempt to match storage drivers,
1163    // should the media object have been re-registered as a result of this
1164    // close.
1165    //
1166
1167    if ( media )
1168    {
1169        media->waitQuiet();
1170        media->release();
1171    }
1172
1173    return 0;
1174}
1175
1176int dkread(dev_t dev, uio_t uio, int /* flags */)
1177{
1178    //
1179    // dkread reads data from a device.
1180    //
1181
1182    struct dio dio = { dev, uio };
1183
1184    return dkreadwrite(&dio, DKRTYPE_DIO);
1185}
1186
1187int dkwrite(dev_t dev, uio_t uio, int /* flags */)
1188{
1189    //
1190    // dkwrite writes data to a device.
1191    //
1192
1193    struct dio dio = { dev, uio };
1194
1195    return dkreadwrite(&dio, DKRTYPE_DIO);
1196}
1197
1198void dkstrategy(buf_t bp)
1199{
1200    //
1201    // dkstrategy starts an asynchronous read or write operation.  It returns
1202    // to the caller as soon as the operation is queued, and completes it via
1203    // the buf_biodone function.
1204    //
1205
1206    dkreadwrite(bp, DKRTYPE_BUF);
1207}
1208
1209int dkioctl(dev_t dev, u_long cmd, caddr_t data, int flags, proc_t proc)
1210{
1211    //
1212    // dkioctl performs operations other than a read or write.
1213    //
1214
1215    int         error = 0;
1216    MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
1217
1218    if ( minor->isOrphaned )  return EBADF;               // (is minor in flux?)
1219
1220    //
1221    // Process the ioctl.
1222    //
1223
1224    switch ( cmd )
1225    {
1226        case DKIOCGETBLOCKSIZE:                                  // (uint32_t *)
1227        {
1228            //
1229            // This ioctl returns the preferred block size of the media object.
1230            //
1231
1232            *(uint32_t *)data = minor->media->getPreferredBlockSize();
1233
1234        } break;
1235
1236#ifndef __LP64__
1237        case DKIOCGETBLOCKCOUNT32:                               // (uint32_t *)
1238        {
1239            //
1240            // This ioctl returns the size of the media object in blocks.  The
1241            // implied block size is returned by DKIOCGETBLOCKSIZE.
1242            //
1243
1244            if ( minor->media->getPreferredBlockSize() )
1245                *(uint32_t *)data = ( minor->media->getSize()               /
1246                                      minor->media->getPreferredBlockSize() );
1247            else
1248                *(uint32_t *)data = 0;
1249
1250        } break;
1251#endif /* !__LP64__ */
1252
1253        case DKIOCGETBLOCKCOUNT:                                 // (uint64_t *)
1254        {
1255            //
1256            // This ioctl returns the size of the media object in blocks.  The
1257            // implied block size is returned by DKIOCGETBLOCKSIZE.
1258            //
1259
1260            if ( minor->media->getPreferredBlockSize() )
1261                *(uint64_t *)data = ( minor->media->getSize()               /
1262                                      minor->media->getPreferredBlockSize() );
1263            else
1264                *(uint64_t *)data = 0;
1265
1266        } break;
1267
1268        case DKIOCGETMAXBLOCKCOUNTREAD:                          // (uint64_t *)
1269        {
1270            //
1271            // This ioctl returns the maximum block count for reads.
1272            //
1273
1274            OSNumber * number = OSDynamicCast(
1275                         /* class  */ OSNumber,
1276                         /* object */ minor->media->getProperty(
1277                                 /* key   */ kIOMaximumBlockCountReadKey,
1278                                 /* plane */ gIOServicePlane ) );
1279            if ( number )
1280                *(uint64_t *)data = number->unsigned64BitValue();
1281            else
1282                *(uint64_t *)data = 0;
1283
1284        } break;
1285
1286        case DKIOCGETMAXBLOCKCOUNTWRITE:                         // (uint64_t *)
1287        {
1288            //
1289            // This ioctl returns the maximum block count for writes.
1290            //
1291
1292            OSNumber * number = OSDynamicCast(
1293                         /* class  */ OSNumber,
1294                         /* object */ minor->media->getProperty(
1295                                 /* key   */ kIOMaximumBlockCountWriteKey,
1296                                 /* plane */ gIOServicePlane ) );
1297            if ( number )
1298                *(uint64_t *)data = number->unsigned64BitValue();
1299            else
1300                *(uint64_t *)data = 0;
1301
1302        } break;
1303
1304        case DKIOCGETMAXBYTECOUNTREAD:                           // (uint64_t *)
1305        {
1306            //
1307            // This ioctl returns the maximum byte count for reads.
1308            //
1309
1310            OSNumber * number = OSDynamicCast(
1311                         /* class  */ OSNumber,
1312                         /* object */ minor->media->getProperty(
1313                                 /* key   */ kIOMaximumByteCountReadKey,
1314                                 /* plane */ gIOServicePlane ) );
1315            if ( number )
1316                *(uint64_t *)data = number->unsigned64BitValue();
1317            else
1318                *(uint64_t *)data = 0;
1319
1320        } break;
1321
1322        case DKIOCGETMAXBYTECOUNTWRITE:                          // (uint64_t *)
1323        {
1324            //
1325            // This ioctl returns the maximum byte count for writes.
1326            //
1327
1328            OSNumber * number = OSDynamicCast(
1329                         /* class  */ OSNumber,
1330                         /* object */ minor->media->getProperty(
1331                                 /* key   */ kIOMaximumByteCountWriteKey,
1332                                 /* plane */ gIOServicePlane ) );
1333            if ( number )
1334                *(uint64_t *)data = number->unsigned64BitValue();
1335            else
1336                *(uint64_t *)data = 0;
1337
1338        } break;
1339
1340        case DKIOCGETMAXSEGMENTCOUNTREAD:                        // (uint64_t *)
1341        {
1342            //
1343            // This ioctl returns the maximum segment count for reads.
1344            //
1345
1346            OSNumber * number = OSDynamicCast(
1347                         /* class  */ OSNumber,
1348                         /* object */ minor->media->getProperty(
1349                                 /* key   */ kIOMaximumSegmentCountReadKey,
1350                                 /* plane */ gIOServicePlane ) );
1351            if ( number )
1352                *(uint64_t *)data = number->unsigned64BitValue();
1353            else
1354                *(uint64_t *)data = 0;
1355
1356        } break;
1357
1358        case DKIOCGETMAXSEGMENTCOUNTWRITE:                       // (uint64_t *)
1359        {
1360            //
1361            // This ioctl returns the maximum segment count for writes.
1362            //
1363
1364            OSNumber * number = OSDynamicCast(
1365                         /* class  */ OSNumber,
1366                         /* object */ minor->media->getProperty(
1367                                 /* key   */ kIOMaximumSegmentCountWriteKey,
1368                                 /* plane */ gIOServicePlane ) );
1369            if ( number )
1370                *(uint64_t *)data = number->unsigned64BitValue();
1371            else
1372                *(uint64_t *)data = 0;
1373
1374        } break;
1375
1376        case DKIOCGETMAXSEGMENTBYTECOUNTREAD:                    // (uint64_t *)
1377        {
1378            //
1379            // This ioctl returns the maximum segment byte count for reads.
1380            //
1381
1382            OSNumber * number = OSDynamicCast(
1383                         /* class  */ OSNumber,
1384                         /* object */ minor->media->getProperty(
1385                                 /* key   */ kIOMaximumSegmentByteCountReadKey,
1386                                 /* plane */ gIOServicePlane ) );
1387            if ( number )
1388                *(uint64_t *)data = number->unsigned64BitValue();
1389            else
1390                *(uint64_t *)data = 0;
1391
1392        } break;
1393
1394        case DKIOCGETMAXSEGMENTBYTECOUNTWRITE:                   // (uint64_t *)
1395        {
1396            //
1397            // This ioctl returns the maximum segment byte count for writes.
1398            //
1399
1400            OSNumber * number = OSDynamicCast(
1401                         /* class  */ OSNumber,
1402                         /* object */ minor->media->getProperty(
1403                                 /* key   */ kIOMaximumSegmentByteCountWriteKey,
1404                                 /* plane */ gIOServicePlane ) );
1405            if ( number )
1406                *(uint64_t *)data = number->unsigned64BitValue();
1407            else
1408                *(uint64_t *)data = 0;
1409
1410        } break;
1411
1412        case DKIOCGETMINSEGMENTALIGNMENTBYTECOUNT:               // (uint64_t *)
1413        {
1414            //
1415            // This ioctl returns the minimum segment alignment in bytes.
1416            //
1417
1418            OSNumber * number = OSDynamicCast(
1419                         /* class  */ OSNumber,
1420                         /* object */ minor->media->getProperty(
1421                                 /* key   */ kIOMinimumSegmentAlignmentByteCountKey,
1422                                 /* plane */ gIOServicePlane ) );
1423            if ( number )
1424                *(uint64_t *)data = number->unsigned64BitValue();
1425            else
1426                *(uint64_t *)data = 0;
1427
1428        } break;
1429
1430        case DKIOCGETMAXSEGMENTADDRESSABLEBITCOUNT:              // (uint64_t *)
1431        {
1432            //
1433            // This ioctl returns the maximum segment width in bits.
1434            //
1435
1436            OSNumber * number = OSDynamicCast(
1437                         /* class  */ OSNumber,
1438                         /* object */ minor->media->getProperty(
1439                                 /* key   */ kIOMaximumSegmentAddressableBitCountKey,
1440                                 /* plane */ gIOServicePlane ) );
1441            if ( number )
1442                *(uint64_t *)data = number->unsigned64BitValue();
1443            else
1444                *(uint64_t *)data = 0;
1445
1446        } break;
1447
1448        case DKIOCGETPHYSICALBLOCKSIZE:                          // (uint32_t *)
1449        {
1450            //
1451            // This ioctl returns the preferred block size of the device.
1452            //
1453
1454            OSNumber * number = OSDynamicCast(
1455                         /* class  */ OSNumber,
1456                         /* object */ minor->media->getProperty(
1457                                 /* key   */ kIOPropertyPhysicalBlockSizeKey,
1458                                 /* plane */ gIOServicePlane ) );
1459            if ( number )
1460                *(uint32_t *)data = number->unsigned32BitValue();
1461            else
1462                *(uint32_t *)data = minor->media->getPreferredBlockSize();
1463
1464        } break;
1465
1466        case DKIOCGETCOMMANDPOOLSIZE:                            // (uint32_t *)
1467        {
1468            //
1469            // This ioctl returns the maximum queue depth of the device.
1470            //
1471
1472            OSNumber * number = OSDynamicCast(
1473                         /* class  */ OSNumber,
1474                         /* object */ minor->media->getProperty(
1475                                 /* key   */ kIOCommandPoolSizeKey,
1476                                 /* plane */ gIOServicePlane ) );
1477            if ( number )
1478                *(uint32_t *)data = number->unsigned32BitValue();
1479            else
1480                *(uint32_t *)data = 0;
1481
1482        } break;
1483
1484        case DKIOCISFORMATTED:                                   // (uint32_t *)
1485        {
1486            //
1487            // This ioctl returns truth if the media object is formatted.
1488            //
1489
1490            *(uint32_t *)data = minor->media->isFormatted();
1491
1492        } break;
1493
1494        case DKIOCISWRITABLE:                                    // (uint32_t *)
1495        {
1496            //
1497            // This ioctl returns truth if the media object is writable.
1498            //
1499
1500            *(uint32_t *)data = minor->media->isWritable();
1501
1502        } break;
1503
1504        case DKIOCEJECT:                                               // (void)
1505        {
1506            //
1507            // This ioctl asks that the media object be ejected from the device.
1508            //
1509
1510            IOMediaBSDClient *     client;
1511            IOBlockStorageDriver * driver;
1512            MinorTable *           minors;
1513            IOReturn               status;
1514
1515            client = minor->client;
1516            driver = (IOBlockStorageDriver *) minor->media->getProvider();
1517            driver = OSDynamicCast(IOBlockStorageDriver, driver);
1518            minors = gIOMediaBSDClientGlobals.getMinors();
1519
1520            // Determine whether this media has an IOBlockStorageDriver parent.
1521
1522            if ( driver == 0 )  { error = ENOTTY;  break; }
1523
1524            // Disable access to opens, closes, tables.
1525
1526            gIOMediaBSDClientGlobals.lockOpen();
1527            gIOMediaBSDClientGlobals.lockState();
1528
1529            // Determine whether there are other opens on the device nodes that
1530            // are associated with this anchor -- the one valid open is the one
1531            // that issued this eject.  If all is well, we then attempt to open
1532            // the block storage driver to make the ejection request.
1533
1534            if ( minors->getOpenCountForAnchorID(minor->anchorID) == 1 &&
1535                 driver->open(client, 0, kIOStorageAccessReaderWriter) )
1536            {
1537                // Mark the minor as being in "open flux".  This means we are in
1538                // a state where the media object has been closed but the device
1539                // node is still open; we must reject all future accesses to the
1540                // device node until it is closed.  Note that we do this both on
1541                // success and failure of the ejection call.
1542
1543                minor->isOrphaned = true;
1544
1545                // Enable access to opens, closes, tables.
1546
1547                gIOMediaBSDClientGlobals.unlockState();
1548                gIOMediaBSDClientGlobals.unlockOpen();
1549
1550                // Close the media object before the ejection request is made.
1551
1552                minor->media->close(client);
1553
1554                // Retain the media's BSD client object, as it is about
1555                // to be terminated, and we still need it for the close.
1556
1557                client->retain();
1558
1559                // Eject the media from the drive.
1560
1561                status = driver->ejectMedia();
1562                error  = driver->errnoFromReturn(status);
1563
1564                // Close the block storage driver.
1565
1566                driver->close(client);
1567
1568                // Release the media's BSD client object.
1569
1570                client->release();
1571            }
1572            else
1573            {
1574                error = EBUSY;
1575
1576                // Enable access to opens, closes, tables.
1577
1578                gIOMediaBSDClientGlobals.unlockState();
1579                gIOMediaBSDClientGlobals.unlockOpen();
1580            }
1581
1582        } break;
1583
1584        case DKIOCFORMAT:                            // (dk_format_capacity_t *)
1585        {
1586            //
1587            // This ioctl asks that the media object be formatted.
1588            //
1589
1590            IOMediaBSDClient *     client;
1591            IOBlockStorageDriver * driver;
1592            MinorTable *           minors;
1593            dk_format_capacity_t * request;
1594            IOReturn               status;
1595
1596            client  = minor->client;
1597            driver  = (IOBlockStorageDriver *) minor->media->getProvider();
1598            driver  = OSDynamicCast(IOBlockStorageDriver, driver);
1599            minors  = gIOMediaBSDClientGlobals.getMinors();
1600            request = (dk_format_capacity_t *) data;
1601
1602            if ( DKIOC_IS_RESERVED(data, 0xF000) )  { error = EINVAL;  break; }
1603
1604            // Determine whether this media has an IOBlockStorageDriver parent.
1605
1606            if ( driver == 0 )  { error = ENOTTY;  break; }
1607
1608            // Disable access to opens, closes, tables.
1609
1610            gIOMediaBSDClientGlobals.lockOpen();
1611            gIOMediaBSDClientGlobals.lockState();
1612
1613            // Determine whether there are other opens on the device nodes that
1614            // are associated with this anchor -- the one valid open is the one
1615            // that issued the format.  If all is well, we then attempt to open
1616            // the block storage driver to make the formatting request.
1617
1618            if ( minors->getOpenCountForAnchorID(minor->anchorID) == 1 &&
1619                 driver->open(client, 0, kIOStorageAccessReaderWriter) )
1620            {
1621                UInt64 capacity = request->blockCount * request->blockSize;
1622
1623                // Mark the minor as being in "open flux".  This means we are in
1624                // a state where the media object has been closed but the device
1625                // node is still open; we must reject all future accesses to the
1626                // device node until it is closed.  Note that we do this both on
1627                // success and failure of the formatting call.
1628
1629                minor->isOrphaned = true;
1630
1631                // Enable access to opens, closes, tables.
1632
1633                gIOMediaBSDClientGlobals.unlockState();
1634                gIOMediaBSDClientGlobals.unlockOpen();
1635
1636                // Close the media object before the formatting request is made.
1637
1638                minor->media->close(client);
1639
1640                // Retain the media's BSD client object, as it is about
1641                // to be terminated, and we still need it for the close.
1642
1643                client->retain();
1644
1645                // Format the media in the drive.
1646
1647                status = driver->formatMedia(capacity);
1648                error  = driver->errnoFromReturn(status);
1649
1650                // Wait until I/O Kit has finished to attempt to match storage
1651                // drivers, since the media object will have been re-published.
1652                // This shall ensure the new IOMediaBSDClient reconnects prior
1653                // to our return from DKIOCFORMAT.  Note that we still recover
1654                // correctly in case the media object doesn't get re-published,
1655                // as though an ejection had taken place.
1656
1657                driver->waitQuiet();
1658
1659                // Close the block storage driver.
1660
1661                driver->close(client);
1662
1663                // Release the media's BSD client object.
1664
1665                client->release();
1666            }
1667            else
1668            {
1669                error = EBUSY;
1670
1671                // Enable access to opens, closes, tables.
1672
1673                gIOMediaBSDClientGlobals.unlockState();
1674                gIOMediaBSDClientGlobals.unlockOpen();
1675            }
1676
1677        } break;
1678
1679        case DKIOCGETFORMATCAPACITIES:             // (dk_format_capacities_t *)
1680        {
1681            //
1682            // This ioctl returns the feasible format capacities for this media
1683            // object.
1684            //
1685
1686            UInt64                      blockSize;
1687            UInt64 *                    capacities;
1688            UInt32                      capacitiesCount;
1689            UInt32                      capacitiesMaxCount;
1690            IOBlockStorageDriver *      driver;
1691            dk_format_capacities_64_t   request;
1692            dk_format_capacities_32_t * request32;
1693            dk_format_capacities_64_t * request64;
1694
1695            driver    = (IOBlockStorageDriver *) minor->media->getProvider();
1696            driver    = OSDynamicCast(IOBlockStorageDriver, driver);
1697            request32 = (dk_format_capacities_32_t *) data;
1698            request64 = (dk_format_capacities_64_t *) data;
1699
1700            if ( proc_is64bit(proc) )
1701            {
1702                if ( DKIOC_IS_RESERVED(data, 0xF000) )  { error = EINVAL;  break; }
1703
1704                request.capacities      = request64->capacities;
1705                request.capacitiesCount = request64->capacitiesCount;
1706            }
1707            else
1708            {
1709                if ( DKIOC_IS_RESERVED(data, 0xFF00) )  { error = EINVAL;  break; }
1710
1711                request.capacities      = request32->capacities;
1712                request.capacitiesCount = request32->capacitiesCount;
1713            }
1714
1715            // Determine whether this media has an IOBlockStorageDriver parent.
1716
1717            if ( driver == 0 )  { error = ENOTTY;  break; }
1718
1719            // Obtain the format capacities list from the block storage driver.
1720
1721            capacitiesCount    = request.capacitiesCount;
1722            capacitiesMaxCount = driver->getFormatCapacities(0, 0);
1723
1724            if ( capacitiesCount )
1725            {
1726                if ( request.capacities == 0 )  { error = EINVAL;  break; }
1727
1728                capacitiesCount = min(capacitiesCount, capacitiesMaxCount);
1729                capacities      = IONew(UInt64, capacitiesCount);
1730
1731                if ( capacities == 0 )  { error = ENOMEM;  break; }
1732
1733                driver->getFormatCapacities(capacities, capacitiesCount);
1734
1735                blockSize = minor->media->getPreferredBlockSize();
1736                if ( blockSize == 0 )  blockSize = DEV_BSIZE;
1737
1738                // Construct the format capacities list for client consumption.
1739
1740                for ( UInt32 index = 0; index < capacitiesCount; index++ )
1741                {
1742                    dk_format_capacity_t capacity = { 0 };
1743
1744                    capacity.blockCount = capacities[index] / blockSize;
1745                    capacity.blockSize  = blockSize;
1746
1747                    if ( proc == kernproc )
1748                    {
1749                        bcopy( /* src */ &capacity,
1750                               /* dst */ (void *) (request.capacities + index * sizeof(dk_format_capacity_t)),
1751                               /* n   */ sizeof(dk_format_capacity_t) );
1752                    }
1753                    else
1754                    {
1755                        error = copyout( /* kaddr */ &capacity,
1756                                         /* uaddr */ request.capacities + index * sizeof(dk_format_capacity_t),
1757                                         /* len   */ sizeof(dk_format_capacity_t) );
1758                    }
1759
1760                    if ( error )  break;
1761                }
1762
1763                IODelete(capacities, UInt64, capacitiesCount);
1764
1765                if ( capacitiesCount < capacitiesMaxCount )  { error = E2BIG; }
1766            }
1767
1768            if ( proc_is64bit(proc) )
1769            {
1770                request64->capacitiesCount = request.capacitiesCount;
1771            }
1772            else
1773            {
1774                request32->capacitiesCount = request.capacitiesCount;
1775            }
1776
1777        } break;
1778
1779        case DKIOCSYNCHRONIZECACHE:                                    // (void)
1780        {
1781            //
1782            // This ioctl asks that the media object be flushed onto the device.
1783            //
1784
1785            IOReturn status;
1786
1787            // Flush the media onto the drive.
1788
1789            status = minor->media->synchronizeCache(minor->client);
1790            error  = minor->media->errnoFromReturn(status);
1791
1792        } break;
1793
1794        case DKIOCUNMAP:                                         // (dk_unmap_t)
1795        {
1796            //
1797            // This ioctl asks that the media object delete unused data.
1798            //
1799
1800            IOStorageExtent * extents;
1801            dk_unmap_64_t     request;
1802            dk_unmap_32_t *   request32;
1803            dk_unmap_64_t *   request64;
1804            IOReturn          status;
1805
1806            assert(sizeof(dk_extent_t) == sizeof(IOStorageExtent));
1807
1808            request32 = (dk_unmap_32_t *) data;
1809            request64 = (dk_unmap_64_t *) data;
1810
1811            if ( proc_is64bit(proc) )
1812            {
1813                request.extents      = request64->extents;
1814                request.extentsCount = request64->extentsCount;
1815                request.options      = request64->options;
1816            }
1817            else
1818            {
1819                if ( DKIOC_IS_RESERVED(data, 0xF000) )  { error = EINVAL;  break; }
1820
1821                request.extents      = request32->extents;
1822                request.extentsCount = request32->extentsCount;
1823                request.options      = request32->options;
1824            }
1825
1826            // Delete unused data from the media.
1827
1828            if ( request.extents == 0 )  { error = EINVAL;  break; }
1829
1830            extents = IONew(IOStorageExtent, request.extentsCount);
1831
1832            if ( extents == 0 )  { error = ENOMEM;  break; }
1833
1834            if ( proc == kernproc )
1835            {
1836                bcopy( /* src */ (void *) request.extents,
1837                       /* dst */ extents,
1838                       /* n   */ request.extentsCount * sizeof(IOStorageExtent) );
1839            }
1840            else
1841            {
1842                error = copyin( /* uaddr */ request.extents,
1843                                /* kaddr */ extents,
1844                                /* len   */ request.extentsCount * sizeof(IOStorageExtent) );
1845            }
1846
1847            if ( error == 0 )
1848            {
1849                status = minor->media->unmap( /* client       */ minor->client,
1850                                              /* extents      */ extents,
1851                                              /* extentsCount */ request.extentsCount,
1852                                              /* options      */ request.options );
1853
1854                error = minor->media->errnoFromReturn(status);
1855            }
1856
1857            IODelete(extents, IOStorageExtent, request.extentsCount);
1858
1859        } break;
1860
1861        case DKIOCREQUESTIDLE:                                         // (void)
1862        {
1863            //
1864            // This ioctl asks that the device enter an idle state.
1865            //
1866
1867            IOBlockStorageDriver * driver;
1868            IOReturn               status;
1869
1870            driver = (IOBlockStorageDriver *) minor->media->getProvider();
1871            driver = OSDynamicCast(IOBlockStorageDriver, driver);
1872
1873            // Determine whether this media has an IOBlockStorageDriver parent.
1874
1875            if ( driver == 0 )  { error = ENOTTY;  break; }
1876
1877            // Request that the drive enter an idle state.
1878
1879            status = driver->requestIdle();
1880            error  = minor->media->errnoFromReturn(status);
1881
1882        } break;
1883
1884        case DKIOCGETBSDUNIT:                                    // (uint32_t *)
1885        {
1886            //
1887            // This ioctl returns the BSD unit of the media object.
1888            //
1889
1890            OSNumber * number = OSDynamicCast(
1891                         /* class  */ OSNumber,
1892                         /* object */ minor->media->getProperty(
1893                                 /* key   */ kIOBSDUnitKey ) );
1894            if ( number )
1895                *(uint32_t *)data = number->unsigned32BitValue();
1896            else
1897                *(uint32_t *)data = 0;
1898
1899        } break;
1900
1901        case DKIOCGETFIRMWAREPATH:                     // (dk_firmware_path_t *)
1902        {
1903            //
1904            // This ioctl returns the open firmware path for this media object.
1905            //
1906
1907            int    l = sizeof(((dk_firmware_path_t *)data)->path);
1908            char * p = ((dk_firmware_path_t *)data)->path;
1909
1910            if ( minor->media->getPath(p, &l, gIODTPlane) && strchr(p, ':') )
1911                strlcpy(p, strchr(p, ':') + 1, l);     // (strip the plane name)
1912            else
1913                error = EINVAL;
1914
1915        } break;
1916
1917        case DKIOCISSOLIDSTATE:                                  // (uint32_t *)
1918        {
1919            //
1920            // This ioctl returns truth if the device is solid state.
1921            //
1922
1923            OSDictionary * dictionary = OSDynamicCast(
1924                         /* class  */ OSDictionary,
1925                         /* object */ minor->media->getProperty(
1926                                 /* key   */ kIOPropertyDeviceCharacteristicsKey,
1927                                 /* plane */ gIOServicePlane ) );
1928
1929            *(uint32_t *)data = false;
1930
1931            if ( dictionary )
1932            {
1933                OSString * string = OSDynamicCast(
1934                         /* class  */ OSString,
1935                         /* object */ dictionary->getObject(
1936                                 /* key   */ kIOPropertyMediumTypeKey ) );
1937
1938                if ( string && string->isEqualTo(kIOPropertyMediumTypeSolidStateKey) )
1939                    *(uint32_t *)data = true;
1940            }
1941
1942        } break;
1943
1944        case DKIOCISVIRTUAL:                                     // (uint32_t *)
1945        {
1946            //
1947            // This ioctl returns truth if the device is virtual.
1948            //
1949
1950            OSDictionary * dictionary = OSDynamicCast(
1951                         /* class  */ OSDictionary,
1952                         /* object */ minor->media->getProperty(
1953                                 /* key   */ kIOPropertyProtocolCharacteristicsKey,
1954                                 /* plane */ gIOServicePlane ) );
1955
1956            *(uint32_t *)data = false;
1957
1958            if ( dictionary )
1959            {
1960                OSString * string = OSDynamicCast(
1961                         /* class  */ OSString,
1962                         /* object */ dictionary->getObject(
1963                                 /* key   */ kIOPropertyPhysicalInterconnectTypeKey ) );
1964
1965                if ( string && string->isEqualTo(kIOPropertyPhysicalInterconnectTypeVirtual) )
1966                    *(uint32_t *)data = true;
1967            }
1968
1969        } break;
1970
1971        case DKIOCGETBASE:                                       // (uint64_t *)
1972        {
1973            //
1974            // This ioctl returns the base of the media object.
1975            //
1976
1977            *(uint64_t *)data = minor->media->getBase();
1978
1979        } break;
1980
1981        case DKIOCGETFEATURES:                                   // (uint32_t *)
1982        {
1983            //
1984            // This ioctl returns the features of the media object.
1985            //
1986
1987            OSDictionary * dictionary = OSDynamicCast(
1988                         /* class  */ OSDictionary,
1989                         /* object */ minor->media->getProperty(
1990                                 /* key   */ kIOStorageFeaturesKey,
1991                                 /* plane */ gIOServicePlane ) );
1992
1993            *(uint32_t *)data = 0;
1994
1995            if ( dictionary )
1996            {
1997                OSBoolean * boolean;
1998
1999                boolean = OSDynamicCast(
2000                         /* class  */ OSBoolean,
2001                         /* object */ dictionary->getObject(
2002                                 /* key   */ kIOStorageFeatureForceUnitAccess ) );
2003
2004                if ( boolean == kOSBooleanTrue )
2005                    *(uint32_t *)data |= DK_FEATURE_FORCE_UNIT_ACCESS;
2006
2007                boolean = OSDynamicCast(
2008                         /* class  */ OSBoolean,
2009                         /* object */ dictionary->getObject(
2010                                 /* key   */ kIOStorageFeaturePriority ) );
2011
2012                if ( boolean == kOSBooleanTrue )
2013                    *(uint32_t *)data |= DK_FEATURE_PRIORITY;
2014
2015                boolean = OSDynamicCast(
2016                         /* class  */ OSBoolean,
2017                         /* object */ dictionary->getObject(
2018                                 /* key   */ kIOStorageFeatureUnmap ) );
2019
2020                if ( boolean == kOSBooleanTrue )
2021                    *(uint32_t *)data |= DK_FEATURE_UNMAP;
2022            }
2023
2024        } break;
2025
2026        case DKIOCGETTHROTTLEMASK:                               // (uint64_t *)
2027        {
2028            //
2029            // This ioctl returns the throttle mask for the media object.
2030            //
2031
2032            *( ( uint64_t * ) data ) = _IOMediaBSDClientGetThrottleMask( minor->media );
2033
2034        } break;
2035
2036        case DKIOCGETENCRYPTIONTYPE:                             // (uint32_t *)
2037        {
2038            //
2039            // This ioctl returns the encryption type of the device.
2040            //
2041
2042            OSDictionary * dictionary = OSDynamicCast(
2043                         /* class  */ OSDictionary,
2044                         /* object */ minor->media->getProperty(
2045                                 /* key   */ kIOPropertyControllerCharacteristicsKey,
2046                                 /* plane */ gIOServicePlane ) );
2047
2048            *(uint32_t *)data = 0;
2049
2050            if ( dictionary )
2051            {
2052                OSString * string = OSDynamicCast(
2053                         /* class  */ OSString,
2054                         /* object */ dictionary->getObject(
2055                                 /* key   */ kIOPropertyEncryptionTypeKey ) );
2056
2057                if ( string )
2058                {
2059                    if ( string->isEqualTo(kIOPropertyAESCBCKey) )
2060                        *(uint32_t *)data = DK_ENCRYPTION_TYPE_AES_CBC;
2061                    else if ( string->isEqualTo(kIOPropertyAESXEXKey) )
2062                        *(uint32_t *)data = DK_ENCRYPTION_TYPE_AES_XEX;
2063                    else if ( string->isEqualTo(kIOPropertyAESXTSKey) )
2064                        *(uint32_t *)data = DK_ENCRYPTION_TYPE_AES_XTS;
2065                }
2066            }
2067
2068        } break;
2069
2070        case DKIOCISLOWPOWERMODE:                                // (uint32_t *)
2071        {
2072            //
2073            // This ioctl returns truth if the device is low power.
2074            //
2075
2076            OSDictionary * dictionary = OSDynamicCast(
2077                         /* class  */ OSDictionary,
2078                         /* object */ minor->media->getProperty(
2079                                 /* key   */ kIOPropertyControllerCharacteristicsKey,
2080                                 /* plane */ gIOServicePlane ) );
2081
2082            *(uint32_t *)data = false;
2083
2084            if ( dictionary )
2085            {
2086                OSBoolean * boolean = OSDynamicCast(
2087                         /* class  */ OSBoolean,
2088                         /* object */ dictionary->getObject(
2089                                 /* key   */ kIOPropertyLowPowerModeKey ) );
2090
2091                if ( boolean == kOSBooleanTrue )
2092                    *(uint32_t *)data = true;
2093            }
2094
2095        } break;
2096
2097        default:
2098        {
2099            //
2100            // Call the foreign ioctl handler for all other ioctls.
2101            //
2102
2103            error = minor->client->ioctl(dev, cmd, data, flags, proc);
2104
2105        } break;
2106    }
2107
2108    return error;                                       // (return error status)
2109}
2110
2111int dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int flags, proc_t proc)
2112{
2113    //
2114    // dkioctl_bdev performs operations other than a read or write, specific to
2115    // the block device.
2116    //
2117
2118    int         error = 0;
2119    MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
2120
2121    if ( minor->isOrphaned )  return EBADF;               // (is minor in flux?)
2122
2123    //
2124    // Process the ioctl.
2125    //
2126
2127    switch ( cmd )
2128    {
2129        case DKIOCGETBLOCKSIZE:                                  // (uint32_t *)
2130        {
2131            //
2132            // This ioctl returns the preferred (or overrided) block size of the
2133            // media object.
2134            //
2135
2136            *(uint32_t *)data = minor->bdevBlockSize;
2137
2138        } break;
2139
2140        case DKIOCSETBLOCKSIZE:                                  // (uint32_t *)
2141        {
2142            //
2143            // This ioctl overrides the block size for the media object, for the
2144            // duration of all block device opens at this minor.
2145            //
2146
2147            if ( *(uint32_t *)data > 0 )
2148                minor->bdevBlockSize = (UInt64) (*(uint32_t *)data);
2149            else
2150                error = EINVAL;
2151
2152        } break;
2153
2154#ifndef __LP64__
2155        case DKIOCGETBLOCKCOUNT32:                               // (uint32_t *)
2156        {
2157            //
2158            // This ioctl returns the size of the media object in blocks.  The
2159            // implied block size is returned by DKIOCGETBLOCKSIZE.
2160            //
2161
2162            if ( minor->bdevBlockSize )
2163                *(uint32_t *)data = ( minor->media->getSize() /
2164                                      minor->bdevBlockSize    );
2165            else
2166                *(uint32_t *)data = 0;
2167
2168        } break;
2169#endif /* !__LP64__ */
2170
2171        case DKIOCGETBLOCKCOUNT:                                 // (uint64_t *)
2172        {
2173            //
2174            // This ioctl returns the size of the media object in blocks.  The
2175            // implied block size is returned by DKIOCGETBLOCKSIZE.
2176            //
2177
2178            if ( minor->bdevBlockSize )
2179                *(uint64_t *)data = ( minor->media->getSize() /
2180                                      minor->bdevBlockSize    );
2181            else
2182                *(uint64_t *)data = 0;
2183
2184        } break;
2185
2186        case DKIOCLOCKPHYSICALEXTENTS:                                 // (void)
2187        {
2188            bool success;
2189
2190            success = minor->media->lockPhysicalExtents( minor->client );
2191
2192            if ( success == false )
2193            {
2194                error = ENOTSUP;
2195            }
2196
2197        } break;
2198
2199        case DKIOCGETPHYSICALEXTENT:                   // (dk_physical_extent_t)
2200        {
2201            dk_physical_extent_t * request;
2202
2203            request = ( dk_physical_extent_t * ) data;
2204
2205            if ( DKIOC_IS_RESERVED( data, 0xFFFF0000 ) == false )
2206            {
2207                IOStorage * media;
2208
2209                media = minor->media->copyPhysicalExtent( minor->client, &request->offset, &request->length );
2210
2211                if ( media )
2212                {
2213                    OSNumber * majorID;
2214
2215                    majorID = OSDynamicCast( OSNumber, media->getProperty( kIOBSDMajorKey ) );
2216
2217                    if ( majorID )
2218                    {
2219                        OSNumber * minorID;
2220
2221                        minorID = OSDynamicCast( OSNumber, media->getProperty( kIOBSDMinorKey ) );
2222
2223                        if ( minorID )
2224                        {
2225                            request->dev = makedev( majorID->unsigned32BitValue( ), minorID->unsigned32BitValue( ) );
2226                        }
2227                        else
2228                        {
2229                            error = ENODEV;
2230                        }
2231                    }
2232                    else
2233                    {
2234                        error = ENODEV;
2235                    }
2236
2237                    media->release( );
2238                }
2239                else
2240                {
2241                    error = ENOTSUP;
2242                }
2243            }
2244            else
2245            {
2246                error = EINVAL;
2247            }
2248
2249        } break;
2250
2251        case DKIOCUNLOCKPHYSICALEXTENTS:                               // (void)
2252        {
2253            minor->media->unlockPhysicalExtents( minor->client );
2254
2255        } break;
2256
2257        case DKIOCSETTIER:                                    // (dk_set_tier_t)
2258        {
2259            //
2260            // This ioctl asks that the media object reprioritize a read or
2261            // write request.
2262            //
2263
2264            IOStorageExtent *  extents;
2265            dk_set_tier_64_t   request;
2266            dk_set_tier_32_t * request32;
2267            dk_set_tier_64_t * request64;
2268            IOReturn           status;
2269
2270            assert(sizeof(dk_extent_t) == sizeof(IOStorageExtent));
2271
2272            request32 = (dk_set_tier_32_t *) data;
2273            request64 = (dk_set_tier_64_t *) data;
2274
2275            if ( proc_is64bit(proc) )
2276            {
2277                if ( DKIOC_IS_RESERVED(data, 0xE000) )  { error = EINVAL;  break; }
2278
2279                request.extents      = request64->extents;
2280                request.extentsCount = request64->extentsCount;
2281                request.tier         = request64->tier;
2282            }
2283            else
2284            {
2285                if ( DKIOC_IS_RESERVED(data, 0xFE00) )  { error = EINVAL;  break; }
2286
2287                request.extents      = request32->extents;
2288                request.extentsCount = request32->extentsCount;
2289                request.tier         = request32->tier;
2290            }
2291
2292            // Reprioritize a read or write request.
2293
2294            if ( request.extents == 0 )  { error = EINVAL;  break; }
2295
2296            extents = IONew(IOStorageExtent, request.extentsCount);
2297
2298            if ( extents == 0 )  { error = ENOMEM;  break; }
2299
2300            if ( proc == kernproc )
2301            {
2302                bcopy( /* src */ (void *) request.extents,
2303                       /* dst */ extents,
2304                       /* n   */ request.extentsCount * sizeof(IOStorageExtent) );
2305            }
2306            else
2307            {
2308                error = copyin( /* uaddr */ request.extents,
2309                                /* kaddr */ extents,
2310                                /* len   */ request.extentsCount * sizeof(IOStorageExtent) );
2311            }
2312
2313            if ( error == 0 )
2314            {
2315                status = minor->media->setPriority( /* client       */ minor->client,
2316                                                    /* extents      */ extents,
2317                                                    /* extentsCount */ request.extentsCount,
2318                                                    /* priority     */ DK_TIER_TO_PRIORITY(request.tier) );
2319
2320                error = minor->media->errnoFromReturn(status);
2321            }
2322
2323            IODelete(extents, IOStorageExtent, request.extentsCount);
2324
2325        } break;
2326
2327        default:
2328        {
2329            //
2330            // Call the common ioctl handler for all other ioctls.
2331            //
2332
2333            error = dkioctl(dev, cmd, data, flags, proc);
2334
2335        } break;
2336    }
2337
2338    return error;                                       // (return error status)
2339}
2340
2341int dkioctl_cdev(dev_t dev, u_long cmd, caddr_t data, int flags, proc_t proc)
2342{
2343    //
2344    // dkioctl_cdev performs operations other than a read or write, specific to
2345    // the character device.
2346    //
2347
2348    int         error = 0;
2349    MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
2350
2351    if ( minor->isOrphaned )  return EBADF;               // (is minor in flux?)
2352
2353    //
2354    // Process the ioctl.
2355    //
2356
2357    switch ( cmd )
2358    {
2359#if TARGET_OS_EMBEDDED
2360        case _DKIOCSETSTATIC:                                          // (void)
2361        {
2362            minor->cdevOptions |= kIOStorageOptionIsStatic;
2363
2364        } break;
2365#endif /* TARGET_OS_EMBEDDED */
2366
2367        default:
2368        {
2369            //
2370            // Call the common ioctl handler for all other ioctls.
2371            //
2372
2373            error = dkioctl(dev, cmd, data, flags, proc);
2374
2375        } break;
2376    }
2377
2378    return error;                                       // (return error status)
2379}
2380
2381int dksize(dev_t dev)
2382{
2383    //
2384    // dksize returns the block size of the media.
2385    //
2386    // This is a departure from BSD 4.4's definition of this function, that is,
2387    // it will not return the size of the disk partition, as would be expected
2388    // in a BSD 4.4 implementation.
2389    //
2390
2391    MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
2392
2393    if ( minor->isOrphaned )  return 0;                   // (is minor in flux?)
2394
2395    return (int) minor->bdevBlockSize;                    // (return block size)
2396}
2397
2398// =============================================================================
2399// Support For BSD Functions
2400
2401extern "C" task_t get_aiotask();
2402
2403inline task_t get_kernel_task()
2404{
2405    return kernel_task;
2406}
2407
2408inline task_t get_user_task()
2409{
2410    task_t task;
2411
2412    task = get_aiotask();
2413
2414    if ( task == 0 )  task = current_task();
2415
2416    return task;
2417}
2418
2419inline dev_t DKR_GET_DEV(dkr_t dkr, dkrtype_t dkrtype)
2420{
2421    return (dkrtype == DKRTYPE_BUF)
2422           ? buf_device((buf_t)dkr)
2423           : ((dio_t)dkr)->dev;
2424}
2425
2426inline UInt64 DKR_GET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype)
2427{
2428    return (dkrtype == DKRTYPE_BUF)
2429           ? buf_count((buf_t)dkr)
2430           : uio_resid(((dio_t)dkr)->uio);
2431}
2432
2433inline UInt64 DKR_GET_BYTE_START(dkr_t dkr, dkrtype_t dkrtype)
2434{
2435    if (dkrtype == DKRTYPE_BUF)
2436    {
2437        buf_t       bp = (buf_t)dkr;
2438        MinorSlot * minor;
2439
2440        minor = gIOMediaBSDClientGlobals.getMinor(getminor(buf_device(bp)));
2441
2442        return (UInt64)buf_blkno(bp) * minor->bdevBlockSize;
2443    }
2444
2445    return uio_offset(((dio_t)dkr)->uio);
2446}
2447
2448inline bool DKR_IS_READ(dkr_t dkr, dkrtype_t dkrtype)
2449{
2450    return (dkrtype == DKRTYPE_BUF)
2451           ? ((buf_flags((buf_t)dkr) & B_READ) == B_READ)
2452           : ((uio_rw(((dio_t)dkr)->uio)) == UIO_READ);
2453}
2454
2455inline bool DKR_IS_ASYNCHRONOUS(dkr_t dkr, dkrtype_t dkrtype)
2456{
2457    return (dkrtype == DKRTYPE_BUF)
2458           ? true
2459           : false;
2460}
2461
2462inline bool DKR_IS_RAW(dkr_t dkr, dkrtype_t dkrtype)
2463{
2464    return (dkrtype == DKRTYPE_BUF)
2465           ? false
2466           : true;
2467}
2468
2469inline void DKR_SET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype, UInt64 bcount)
2470{
2471    if (dkrtype == DKRTYPE_BUF)
2472        buf_setresid((buf_t)dkr, buf_count((buf_t)dkr) - bcount);
2473    else
2474        uio_setresid(((dio_t)dkr)->uio, uio_resid(((dio_t)dkr)->uio) - bcount);
2475}
2476
2477inline void DKR_RUN_COMPLETION(dkr_t dkr, dkrtype_t dkrtype, IOReturn status)
2478{
2479    if (dkrtype == DKRTYPE_BUF)
2480    {
2481        buf_t       bp = (buf_t)dkr;
2482        MinorSlot * minor;
2483
2484        minor = gIOMediaBSDClientGlobals.getMinor(getminor(buf_device(bp)));
2485
2486        buf_seterror(bp, minor->media->errnoFromReturn(status));     // (error?)
2487        buf_biodone(bp);                                   // (complete request)
2488    }
2489}
2490
2491inline IOMemoryDescriptor * DKR_GET_BUFFER(dkr_t dkr, dkrtype_t dkrtype)
2492{
2493    if (dkrtype == DKRTYPE_BUF)
2494    {
2495        buf_t bp = (buf_t)dkr;
2496        int   flags;
2497
2498        flags = buf_flags(bp);
2499
2500        if ( (flags & B_CLUSTER) )
2501        {
2502            IOOptionBits options = kIOMemoryTypeUPL | kIOMemoryAsReference;
2503
2504            options |= (flags & B_READ) ? kIODirectionIn : kIODirectionOut;
2505
2506            return IOMemoryDescriptor::withOptions(          // (multiple-range)
2507                buf_upl(bp),
2508                buf_count(bp),
2509                buf_uploffset(bp),
2510                0,
2511                options );
2512        }
2513        else
2514        {
2515            return IOMemoryDescriptor::withAddressRange(       // (single-range)
2516                buf_dataptr(bp),
2517                buf_count(bp),
2518                (flags & B_READ) ? kIODirectionIn : kIODirectionOut,
2519                (flags & B_PHYS) ? get_user_task() : get_kernel_task() );
2520        }
2521    }
2522    else
2523    {
2524        IOOptionBits options = kIOMemoryTypeUIO | kIOMemoryAsReference;
2525        uio_t        uio     = ((dio_t)dkr)->uio;
2526
2527        options |= (uio_rw(uio) == UIO_READ) ? kIODirectionIn : kIODirectionOut;
2528
2529        return IOMemoryDescriptor::withOptions(              // (multiple-range)
2530            uio,
2531            uio_iovcnt(uio),
2532            0,
2533            (uio_isuserspace(uio)) ? get_user_task() : get_kernel_task(),
2534            options );
2535    }
2536}
2537
2538inline void * DKR_GET_DRIVER_DATA(dkr_t dkr, dkrtype_t dkrtype)
2539{
2540    return (dkrtype == DKRTYPE_BUF)
2541           ? buf_drvdata((buf_t)dkr)
2542           : ((dio_t)dkr)->drvdata;
2543}
2544
2545inline void DKR_SET_DRIVER_DATA(dkr_t dkr, dkrtype_t dkrtype, void * drvdata)
2546{
2547    if (dkrtype == DKRTYPE_BUF)
2548        buf_setdrvdata((buf_t)dkr, drvdata);
2549    else
2550        ((dio_t)dkr)->drvdata = drvdata;
2551}
2552
2553inline IOStorageAttributes DKR_GET_ATTRIBUTES(dkr_t dkr, dkrtype_t dkrtype)
2554{
2555    IOStorageAttributes attributes = { 0 };
2556
2557    if (dkrtype == DKRTYPE_BUF)
2558    {
2559        buf_t bp = (buf_t)dkr;
2560        int   flags;
2561
2562        flags = buf_flags(bp);
2563
2564        attributes.bufattr = buf_attr(bp);
2565
2566        attributes.options |= (flags & B_FUA          ) ? kIOStorageOptionForceUnitAccess : 0;
2567        attributes.options |= (flags & B_ENCRYPTED_IO ) ? kIOStorageOptionIsEncrypted     : 0;
2568        attributes.options |= (flags & B_STATICCONTENT) ? kIOStorageOptionIsStatic        : 0;
2569
2570        attributes.priority = DK_TIER_TO_PRIORITY(bufattr_throttled(attributes.bufattr));
2571    }
2572#if TARGET_OS_EMBEDDED
2573    else
2574    {
2575        dev_t       dev = ((dio_t)dkr)->dev;
2576        MinorSlot * minor;
2577
2578        minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
2579
2580        attributes.options |= minor->cdevOptions;
2581    }
2582#endif /* TARGET_OS_EMBEDDED */
2583
2584    return attributes;
2585}
2586///w:start
2587#if !TARGET_OS_EMBEDDED
2588inline bool DKR_DELAY_IDLE_SLEEP(dkr_t dkr, dkrtype_t dkrtype)
2589{
2590    return (dkrtype == DKRTYPE_BUF)
2591           ? bufattr_delayidlesleep(buf_attr((buf_t)dkr))
2592           : false;
2593}
2594#endif /* !TARGET_OS_EMBEDDED */
2595///w:stop
2596
2597int dkreadwrite(dkr_t dkr, dkrtype_t dkrtype)
2598{
2599    //
2600    // dkreadwrite performs a read or write operation.
2601    //
2602
2603    IOStorageAttributes  attributes;
2604    IOMemoryDescriptor * buffer;
2605    register UInt64      byteCount;
2606    register UInt64      byteStart;
2607    UInt64               mediaSize;
2608    MinorSlot *          minor;
2609    IOReturn             status;
2610
2611    DKR_SET_DRIVER_DATA(dkr, dkrtype, 0);
2612
2613    minor = gIOMediaBSDClientGlobals.getMinor(getminor(DKR_GET_DEV(dkr, dkrtype)));
2614
2615    if ( minor->isOrphaned )                              // (is minor in flux?)
2616    {
2617        status = kIOReturnNoMedia;
2618        goto dkreadwriteErr;
2619    }
2620
2621    if ( minor->media->isFormatted() == false )       // (is media unformatted?)
2622    {
2623        status = kIOReturnUnformattedMedia;
2624        goto dkreadwriteErr;
2625    }
2626
2627    byteCount = DKR_GET_BYTE_COUNT(dkr, dkrtype);            // (get byte count)
2628    byteStart = DKR_GET_BYTE_START(dkr, dkrtype);            // (get byte start)
2629    mediaSize = minor->media->getSize();                     // (get media size)
2630
2631    //
2632    // Reads that start at (or perhaps past) the end-of-media are not considered
2633    // errors, even though no data is transferred, while writes at (or past) the
2634    // end-of-media do indeed return errors under BSD semantics.
2635    //
2636
2637    if ( byteStart >= mediaSize )     // (is start at or past the end-of-media?)
2638    {
2639        status = DKR_IS_READ(dkr,dkrtype) ? kIOReturnSuccess : kIOReturnIOError;
2640        goto dkreadwriteErr;
2641    }
2642
2643    //
2644    // Reads and writes, via the character device, that do not start or end on a
2645    // media block boundary are considered errors under BSD semantics.
2646    //
2647
2648    if ( DKR_IS_RAW(dkr, dkrtype) )
2649    {
2650        UInt64 mediaBlockSize = minor->media->getPreferredBlockSize();
2651
2652        if ( (byteStart % mediaBlockSize) || (byteCount % mediaBlockSize) )
2653        {
2654            status = kIOReturnNotAligned;
2655            goto dkreadwriteErr;
2656        }
2657    }
2658
2659    //
2660    // Build a descriptor which describes the buffer involved in the transfer.
2661    //
2662
2663    buffer = DKR_GET_BUFFER(dkr, dkrtype);
2664
2665    if ( buffer == 0 )                                           // (no buffer?)
2666    {
2667        status = kIOReturnNoMemory;
2668        goto dkreadwriteErr;
2669    }
2670
2671    //
2672    // Reads and writes that extend beyond the end-of-media are not considered
2673    // errors under BSD semantics.  We are to transfer as many bytes as can be
2674    // read or written from the medium and return no error.  This differs from
2675    // IOMedia semantics which is to fail the entire request without copying a
2676    // single byte should it include something past the end-of-media.  We must
2677    // adapt the IOMedia semantics to look like BSD semantics here.
2678    //
2679    // Clip the transfer buffer should this be a short read or write request.
2680    //
2681
2682    if ( byteCount > mediaSize - byteStart )           // (clip at end-of-media)
2683    {
2684        IOMemoryDescriptor * originalBuffer = buffer;
2685
2686        buffer = IOSubMemoryDescriptor::withSubRange(
2687                           /* descriptor    */ originalBuffer,
2688                           /* withOffset    */ 0,
2689                           /* withLength    */ mediaSize - byteStart,
2690                           /* withDirection */ originalBuffer->getDirection() );
2691
2692        originalBuffer->release();   // (either retained above or about to fail)
2693
2694        if ( buffer == 0 )                                      // (no buffer?)
2695        {
2696            status = kIOReturnNoMemory;
2697            goto dkreadwriteErr;
2698        }
2699    }
2700
2701    //
2702    // Prepare the transfer.
2703    //
2704
2705    if ( buffer->prepare() != kIOReturnSuccess )         // (prepare the buffer)
2706    {
2707        buffer->release();
2708        status = kIOReturnVMError;            // (wiring or permissions failure)
2709        goto dkreadwriteErr;
2710    }
2711
2712    //
2713    // Execute the transfer.
2714    //
2715
2716    attributes = DKR_GET_ATTRIBUTES(dkr, dkrtype);
2717
2718    DKR_SET_DRIVER_DATA(dkr, dkrtype, buffer);
2719
2720///w:start
2721#if !TARGET_OS_EMBEDDED
2722    if ( DKR_DELAY_IDLE_SLEEP(dkr, dkrtype) )
2723    {
2724        IOPMDriverAssertionID assertionID;
2725        AbsoluteTime          assertionTime;
2726
2727        gIOMediaBSDClientGlobals.lockAssertion();
2728
2729        clock_interval_to_deadline(60, NSEC_PER_SEC, &assertionTime);
2730
2731        gIOMediaBSDClientGlobals.setAssertionTime(assertionTime);
2732
2733        assertionID = gIOMediaBSDClientGlobals.getAssertionID();
2734
2735        if ( assertionID == kIOPMUndefinedDriverAssertionID )
2736        {
2737            assertionID = IOService::getPMRootDomain()->createPMAssertion(
2738                    /* type        */ kIOPMDriverAssertionReservedBit7,
2739                    /* level       */ kIOPMDriverAssertionLevelOn,
2740                    /* service     */ minor->client,
2741                    /* description */ "com.apple.iokit.IOStorageFamily" );
2742
2743            if ( assertionID != kIOPMUndefinedDriverAssertionID )
2744            {
2745                gIOMediaBSDClientGlobals.setAssertionID(assertionID);
2746
2747                thread_call_enter_delayed(
2748                        /* call        */ gIOMediaBSDClientGlobals.getAssertionCall(),
2749                        /* deadline    */ assertionTime );
2750            }
2751        }
2752
2753        gIOMediaBSDClientGlobals.unlockAssertion();
2754    }
2755#endif /* !TARGET_OS_EMBEDDED */
2756///w:stop
2757    if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) )       // (an asynchronous request?)
2758    {
2759        IOStorageCompletion completion;
2760
2761        completion.target    = dkr;
2762        completion.action    = dkreadwritecompletion;
2763        completion.parameter = (void *) dkrtype;
2764
2765        if ( DKR_IS_READ(dkr, dkrtype) )                            // (a read?)
2766        {
2767            minor->media->read(  /* client     */ minor->client,
2768                                 /* byteStart  */ byteStart,
2769                                 /* buffer     */ buffer,
2770                                 /* attributes */ &attributes,
2771                                 /* completion */ &completion );         // (go)
2772        }
2773        else                                                       // (a write?)
2774        {
2775            minor->media->write( /* client     */ minor->client,
2776                                 /* byteStart  */ byteStart,
2777                                 /* buffer     */ buffer,
2778                                 /* attributes */ &attributes,
2779                                 /* completion */ &completion );         // (go)
2780        }
2781
2782        status = kIOReturnSuccess;
2783    }
2784    else                                             // (a synchronous request?)
2785    {
2786        if ( DKR_IS_READ(dkr, dkrtype) )                            // (a read?)
2787        {
2788            status = minor->media->read(
2789                                 /* client          */ minor->client,
2790                                 /* byteStart       */ byteStart,
2791                                 /* buffer          */ buffer,
2792                                 /* attributes      */ &attributes,
2793                                 /* actualByteCount */ &byteCount );     // (go)
2794        }
2795        else                                                       // (a write?)
2796        {
2797            status = minor->media->write(
2798                                 /* client          */ minor->client,
2799                                 /* byteStart       */ byteStart,
2800                                 /* buffer          */ buffer,
2801                                 /* attributes      */ &attributes,
2802                                 /* actualByteCount */ &byteCount );     // (go)
2803        }
2804
2805        dkreadwritecompletion(dkr, (void *)dkrtype, status, byteCount);
2806    }
2807
2808    return minor->media->errnoFromReturn(status);       // (return error status)
2809
2810dkreadwriteErr:
2811
2812    dkreadwritecompletion(dkr, (void *)dkrtype, status, 0);
2813
2814    return minor->media->errnoFromReturn(status);       // (return error status)
2815}
2816
2817void dkreadwritecompletion( void *   target,
2818                            void *   parameter,
2819                            IOReturn status,
2820                            UInt64   actualByteCount )
2821{
2822    //
2823    // dkreadwritecompletion cleans up after a read or write operation.
2824    //
2825
2826    dkr_t       dkr     = (dkr_t) target;
2827    dkrtype_t   dkrtype = (dkrtype_t) (uintptr_t) parameter;
2828    dev_t       dev     = DKR_GET_DEV(dkr, dkrtype);
2829    void *      drvdata = DKR_GET_DRIVER_DATA(dkr, dkrtype);
2830    MinorSlot * minor   = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
2831
2832    if ( drvdata )                                            // (has a buffer?)
2833    {
2834        IOMemoryDescriptor * buffer = (IOMemoryDescriptor *) drvdata;
2835
2836        buffer->complete();                             // (complete the buffer)
2837        buffer->release();                 // (release our retain on the buffer)
2838    }
2839
2840    if ( status != kIOReturnSuccess )                         // (has an error?)
2841    {
2842        if ( status != kIOReturnNotPermitted )
2843        {
2844            IOLog("%s: %s.\n", minor->name, minor->media->stringFromReturn(status));
2845        }
2846    }
2847
2848    if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) )       // (an asynchronous request?)
2849    {
2850        DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount);   // (set byte count)
2851        DKR_RUN_COMPLETION(dkr, dkrtype, status);            // (run completion)
2852    }
2853    else
2854    {
2855        DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount);   // (set byte count)
2856    }
2857}
2858///w:start
2859#if !TARGET_OS_EMBEDDED
2860void dkreadwriteassertion(thread_call_param_t param0, thread_call_param_t param1)
2861{
2862    AbsoluteTime assertionTime;
2863
2864    gIOMediaBSDClientGlobals.lockAssertion();
2865
2866    assertionTime = gIOMediaBSDClientGlobals.getAssertionTime();
2867
2868    if ( __OSAbsoluteTime(assertionTime) < mach_absolute_time() )
2869    {
2870        IOPMDriverAssertionID assertionID;
2871
2872        assertionID = gIOMediaBSDClientGlobals.getAssertionID();
2873
2874        IOService::getPMRootDomain()->releasePMAssertion(assertionID);
2875
2876        gIOMediaBSDClientGlobals.setAssertionID(kIOPMUndefinedDriverAssertionID);
2877    }
2878    else
2879    {
2880        thread_call_enter_delayed(
2881                /* call        */ gIOMediaBSDClientGlobals.getAssertionCall(),
2882                /* deadline    */ assertionTime );
2883    }
2884
2885    gIOMediaBSDClientGlobals.unlockAssertion();
2886}
2887#endif /* !TARGET_OS_EMBEDDED */
2888///w:stop
2889
2890// =============================================================================
2891// AnchorTable Class
2892
2893AnchorTable::AnchorTable()
2894{
2895    //
2896    // Initialize this object's minimal state.
2897    //
2898
2899    _table      = 0;
2900    _tableCount = 0;
2901}
2902
2903AnchorTable::~AnchorTable()
2904{
2905    //
2906    // Free all of this object's outstanding resources.
2907    //
2908
2909    for ( UInt32 anchorID = 0; anchorID < _tableCount; anchorID++ )
2910        if ( _table[anchorID].isAssigned )  remove(anchorID);
2911
2912    if ( _table )  IODelete(_table, AnchorSlot, _tableCount);
2913}
2914
2915UInt32 AnchorTable::insert(IOService * anchor, void * key)
2916{
2917    //
2918    // This method inserts the specified anchor into an unassigned slot in the
2919    // anchor table and returns its ID (or kInvalidAnchorID on a failure).
2920    //
2921    // Note that the anchor is transparently removed from the table should the
2922    // anchor terminate (or it is at least marked obsolete,  should references
2923    // to the anchor still exist in the minor table).
2924    //
2925
2926    UInt32       anchorID;
2927    IONotifier * notifier;
2928
2929    // Search for an unassigned slot in the anchor table.
2930
2931    for ( anchorID = 0; anchorID < _tableCount; anchorID++ )
2932        if ( _table[anchorID].isAssigned == false )  break;
2933
2934    // Was an unassigned slot found?  If not, grow the table.
2935
2936    if ( anchorID == _tableCount )
2937    {
2938        AnchorSlot * newTable;
2939        UInt32       newTableCount;
2940
2941        // We must expand the anchor table since no more slots are available.
2942
2943        if ( _tableCount >= kAnchorsMaxCount )  return kInvalidAnchorID;
2944
2945        newTableCount = min(kAnchorsAddCount + _tableCount, kAnchorsMaxCount);
2946        newTable      = IONew(AnchorSlot, newTableCount);
2947
2948        if ( newTable == 0 )  return kInvalidAnchorID;
2949
2950        bzero(newTable, newTableCount * sizeof(AnchorSlot));
2951
2952        // Copy over the old table's entries, then free the old table.
2953
2954        if ( _table )
2955        {
2956            bcopy(_table, newTable, _tableCount * sizeof(AnchorSlot));
2957            IODelete(_table, AnchorSlot, _tableCount);
2958        }
2959
2960        // Obtain the next unassigned index (simple since we know the size of
2961        // the old table),  then update our instance variables to reflect the
2962        // new tables.
2963
2964        anchorID    = _tableCount;
2965        _table      = newTable;
2966        _tableCount = newTableCount;
2967    }
2968
2969    // Create a notification handler for the anchor's termination (post-stop);
2970    // the handler will remove the anchor transparently from the table if the
2971    // anchor terminates (or at least marks it obsolete, if references to the
2972    // anchor still exist in the minor table).
2973
2974    notifier = anchor->registerInterest(
2975                          /* type        */ gIOGeneralInterest,
2976                          /* action      */ anchorWasNotified,
2977                          /* target      */ this,
2978                          /* parameter   */ 0 );
2979
2980    if ( notifier == 0 )  return kInvalidAnchorID;
2981
2982    // Zero the new slot, fill it in, and retain the anchor object.
2983
2984    bzero(&_table[anchorID], sizeof(AnchorSlot)); // (zero slot)
2985
2986    _table[anchorID].isAssigned = true;           // (fill in slot)
2987    _table[anchorID].isObsolete = false;
2988    _table[anchorID].anchor     = anchor;
2989    _table[anchorID].key        = key;
2990    _table[anchorID].notifier   = notifier;
2991
2992    _table[anchorID].anchor->retain();            // (retain anchor)
2993
2994    return anchorID;
2995}
2996
2997void AnchorTable::remove(UInt32 anchorID)
2998{
2999    //
3000    // This method removes the specified anchor from the anchor table.
3001    //
3002
3003    assert(anchorID < _tableCount);
3004    assert(_table[anchorID].isAssigned);
3005
3006    // Release the resources retained in the anchor slot and zero it.
3007
3008    _table[anchorID].notifier->remove();
3009    _table[anchorID].anchor->release();           // (release anchor)
3010
3011    bzero(&_table[anchorID], sizeof(AnchorSlot)); // (zero slot)
3012}
3013
3014void AnchorTable::obsolete(UInt32 anchorID)
3015{
3016    //
3017    // This method obsoletes the specified anchor, that is, the slot is marked
3018    // as obsolete and will be removed later via the minor table remove method
3019    // once it detects references to the anchor ID drop to 0.   Once obsoleted,
3020    // the anchor can be considered to be removed, since it will not appear in
3021    // locate searches, even though behind the scenes it still occupies a slot.
3022    //
3023
3024    assert(anchorID < _tableCount);
3025    assert(_table[anchorID].isAssigned);
3026
3027    // Mark the anchor as obsolete so that it can be removed from the table as
3028    // soon as all its references go away (minor table's responsibility).
3029
3030    _table[anchorID].isObsolete = true;
3031}
3032
3033UInt32 AnchorTable::locate(IOService * anchor)
3034{
3035    //
3036    // This method searches for the specified anchor in the anchor table and
3037    // returns its ID (or kInvalidAnchorID on a failure).  It would find the
3038    // first occurrence of the anchor in case multiple entries with the same
3039    // anchor object exist.  It ignores slots marked as obsolete.
3040    //
3041
3042    for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
3043    {
3044        if ( _table[anchorID].isAssigned != false  &&
3045             _table[anchorID].isObsolete == false  &&
3046             _table[anchorID].anchor     == anchor )  return anchorID;
3047    }
3048
3049    return kInvalidAnchorID;
3050}
3051
3052UInt32 AnchorTable::locate(IOService * anchor, void * key)
3053{
3054    //
3055    // This method searches for the specified anchor and key pair in the anchor
3056    // table and returns its ID (or kInvalidAnchorID on a failure).  It ignores
3057    // slots marked as obsolete.
3058    //
3059
3060    for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
3061    {
3062        if ( _table[anchorID].isAssigned != false  &&
3063             _table[anchorID].isObsolete == false  &&
3064             _table[anchorID].anchor     == anchor &&
3065             _table[anchorID].key        == key    )  return anchorID;
3066    }
3067
3068    return kInvalidAnchorID;
3069}
3070
3071UInt32 AnchorTable::update(IOService * anchor, void * key)
3072{
3073    //
3074    // This method searches for the specified anchor in the anchor table and
3075    // updates its key value if no references to it exist in the minor table
3076    // or if the references in the minor table are all obsolete.  It returns
3077    // the updated anchor ID (or kInvalidAnchorID on a failure).  It ignores
3078    // slots marked as obsolete.
3079    //
3080
3081    MinorTable * minors = gIOMediaBSDClientGlobals.getMinors();
3082
3083    for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
3084    {
3085        if ( _table[anchorID].isAssigned != false  &&
3086             _table[anchorID].isObsolete == false  &&
3087             _table[anchorID].anchor     == anchor )
3088        {
3089            if ( minors->hasReferencesToAnchorID(anchorID, true) == false )
3090            {
3091                _table[anchorID].key = key;
3092                return anchorID;
3093            }
3094        }
3095    }
3096
3097    return kInvalidAnchorID;
3098}
3099
3100bool AnchorTable::isObsolete(UInt32 anchorID)
3101{
3102    //
3103    // Determine whether the specified anchor ID is marked as obsolete.
3104    //
3105
3106    assert(anchorID < _tableCount);
3107    assert(_table[anchorID].isAssigned);
3108
3109    return _table[anchorID].isObsolete ? true : false;
3110}
3111
3112IOReturn AnchorTable::anchorWasNotified( void *      /* target */,
3113                                         void *      /* parameter */,
3114                                         UInt32      messageType,
3115                                         IOService * anchor,
3116                                         void *      /* messageArgument */,
3117                                         vm_size_t   /* messageArgumentSize */ )
3118{
3119    //
3120    // Notification handler for anchors.
3121    //
3122
3123    AnchorTable * anchors = gIOMediaBSDClientGlobals.getAnchors();
3124    UInt32        anchorID;
3125    MinorTable *  minors  = gIOMediaBSDClientGlobals.getMinors();
3126
3127    // Determine whether this is a termination notification (post-stop).
3128
3129    if ( messageType != kIOMessageServiceIsTerminated )
3130        return kIOReturnSuccess;
3131
3132    // Disable access to tables.
3133
3134    gIOMediaBSDClientGlobals.lockState();
3135
3136    // Determine whether this anchor is in the anchor table (obsolete occurences
3137    // are skipped in the search, as appropriate, since those anchor IDs will be
3138    // removed as it is).
3139
3140    while ( (anchorID = anchors->locate(anchor)) != kInvalidAnchorID )
3141    {
3142        // Determine whether this anchor still has references from the minor
3143        // table.  If it does, we mark the the anchor as obsolete so that it
3144        // will be removed later, once references to it go to zero (which is
3145        // handled by MinorTable::remove).
3146
3147        if ( minors->hasReferencesToAnchorID(anchorID, false) )
3148            anchors->obsolete(anchorID);
3149        else
3150            anchors->remove(anchorID);
3151    }
3152
3153    // Enable access to tables.
3154
3155    gIOMediaBSDClientGlobals.unlockState();
3156
3157    return kIOReturnSuccess;
3158}
3159
3160// =============================================================================
3161// MinorTable Class
3162
3163MinorTable::MinorTable()
3164{
3165    //
3166    // Initialize this object's minimal state.
3167    //
3168
3169    _table.buckets = IONew(MinorSlot *, kMinorsBucketCount);
3170    _tableCount    = 0;
3171
3172    if ( _table.buckets )
3173        bzero(_table.buckets, kMinorsBucketCount * sizeof(MinorSlot *));
3174}
3175
3176MinorTable::~MinorTable()
3177{
3178    //
3179    // Free all of this object's outstanding resources.
3180    //
3181
3182    for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
3183        if ( _table[minorID].isAssigned )  remove(minorID);
3184
3185    if ( _table.buckets )
3186    {
3187        for ( UInt32 bucketID = 0; _table.buckets[bucketID]; bucketID++ )
3188            IODelete(_table.buckets[bucketID], MinorSlot, kMinorsAddCount);
3189
3190        IODelete(_table.buckets, MinorSlot *, kMinorsBucketCount);
3191    }
3192}
3193
3194UInt32 MinorTable::insert( IOMedia *          media,
3195                           UInt32             anchorID,
3196                           IOMediaBSDClient * client,
3197                           char *             slicePath )
3198{
3199    //
3200    // This method inserts the specified media/anchorID pair into an unassigned
3201    // slot in the minor table and returns its ID (or kInvalidMinorID on error).
3202    //
3203    // Note that the bdev and cdev nodes are published as a result of this call,
3204    // with the name "[r]disk<anchorID><slicePath>".  For instance, "disk2s3s1"
3205    // for an anchorID of 2 and slicePath of "s3s1".
3206    //
3207
3208    void *       bdevNode;
3209    void *       cdevNode;
3210    UInt32       majorID = gIOMediaBSDClientGlobals.getMajorID();
3211    UInt32       minorID;
3212    char *       minorName;
3213    UInt32       minorNameSize;
3214
3215    if ( _table.buckets == 0 )  return kInvalidMinorID;
3216
3217    // Search for an unassigned slot in the minor table.
3218
3219    for ( minorID = 0; minorID < _tableCount; minorID++ )
3220        if ( _table[minorID].isAssigned == false )  break;
3221
3222    // Was an unassigned slot found?  If not, grow the table.
3223
3224    if ( minorID == _tableCount )
3225    {
3226        UInt32 bucketID = _tableCount / kMinorsAddCount;
3227
3228        // We must expand the minor table since no more slots are available.
3229
3230        if ( bucketID >= kMinorsBucketCount )  return kInvalidMinorID;
3231
3232        _table.buckets[bucketID] = IONew(MinorSlot, kMinorsAddCount);
3233
3234        if ( _table.buckets[bucketID] == 0 )  return kInvalidMinorID;
3235
3236        bzero(_table.buckets[bucketID], kMinorsAddCount * sizeof(MinorSlot));
3237
3238        _tableCount += kMinorsAddCount;
3239    }
3240
3241    // Create a buffer large enough to hold the full name of the minor.
3242
3243    minorNameSize = strlen("disk#");
3244    for (unsigned temp = anchorID; temp >= 10; temp /= 10)  minorNameSize++;
3245    minorNameSize += strlen(slicePath);
3246    minorNameSize += 1;
3247    minorName = IONew(char, minorNameSize);
3248
3249    // Create a block and character device node in BSD for this media.
3250
3251    bdevNode = devfs_make_node( /* dev        */ makedev(majorID, minorID),
3252                                /* type       */ DEVFS_BLOCK,
3253                                /* owner      */ UID_ROOT,
3254                                /* group      */ GID_OPERATOR,
3255                                /* permission */ 0640,
3256                                /* name (fmt) */ "disk%d%s",
3257                                /* name (arg) */ anchorID,
3258                                /* name (arg) */ slicePath );
3259
3260    cdevNode = devfs_make_node( /* dev        */ makedev(majorID, minorID),
3261                                /* type       */ DEVFS_CHAR,
3262                                /* owner      */ UID_ROOT,
3263                                /* group      */ GID_OPERATOR,
3264                                /* permission */ 0640,
3265                                /* name (fmt) */ "rdisk%d%s",
3266                                /* name (arg) */ anchorID,
3267                                /* name (arg) */ slicePath );
3268
3269    if ( minorName == 0 || bdevNode == 0 || cdevNode == 0 )
3270    {
3271        if ( cdevNode )   devfs_remove(cdevNode);
3272        if ( bdevNode )   devfs_remove(bdevNode);
3273        if ( minorName )  IODelete(minorName, char, minorNameSize);
3274
3275        return kInvalidMinorID;
3276    }
3277
3278    // Construct a name for the node.
3279
3280    snprintf(minorName, minorNameSize, "disk%d%s", (int) anchorID, slicePath);
3281    assert(strlen(minorName) + 1 == minorNameSize);
3282
3283    // Zero the new slot, fill it in, and retain the appropriate objects.
3284
3285    bzero(&_table[minorID], sizeof(MinorSlot));    // (zero slot)
3286
3287    _table[minorID].isAssigned    = true;          // (fill in slot)
3288    _table[minorID].isObsolete    = false;
3289    _table[minorID].isOrphaned    = false;
3290    _table[minorID].anchorID      = anchorID;
3291    _table[minorID].client        = client;
3292    _table[minorID].media         = media;
3293    _table[minorID].name          = minorName;
3294    _table[minorID].bdevBlockSize = media->getPreferredBlockSize();
3295    _table[minorID].bdevNode      = bdevNode;
3296    _table[minorID].bdevOpen      = 0;
3297    _table[minorID].bdevOpenLevel = kIOStorageAccessNone;
3298    _table[minorID].cdevNode      = cdevNode;
3299    _table[minorID].cdevOpen      = 0;
3300    _table[minorID].cdevOpenLevel = kIOStorageAccessNone;
3301#if TARGET_OS_EMBEDDED
3302    _table[minorID].cdevOptions   = 0;
3303#endif /* TARGET_OS_EMBEDDED */
3304
3305    _table[minorID].client->retain();              // (retain client)
3306    _table[minorID].media->retain();               // (retain media)
3307
3308    return minorID;
3309}
3310
3311void MinorTable::remove(UInt32 minorID)
3312{
3313    //
3314    // This method removes the specified minor from the minor table.
3315    //
3316
3317    UInt32 anchorID;
3318
3319    assert(minorID < _tableCount);
3320    assert(_table[minorID].isAssigned);
3321
3322    assert(_table[minorID].isOrphaned == false);
3323    assert(_table[minorID].bdevOpen == 0);
3324    assert(_table[minorID].cdevOpen == 0);
3325
3326    anchorID = _table[minorID].anchorID;
3327
3328    // Release the resources retained in the minor slot and zero it.
3329
3330    devfs_remove(_table[minorID].cdevNode);
3331    devfs_remove(_table[minorID].bdevNode);
3332    IODelete(_table[minorID].name, char, strlen(_table[minorID].name) + 1);
3333    _table[minorID].client->release();             // (release client)
3334    _table[minorID].media->release();              // (release media)
3335
3336    bzero(&_table[minorID], sizeof(MinorSlot));    // (zero slot)
3337
3338    // Determine whether the associated anchor ID is marked as obsolete.  If it
3339    // is and there are no other references to the anchor ID in the minor table,
3340    // we remove the anchor ID from the anchor table.
3341
3342    if ( gIOMediaBSDClientGlobals.getAnchors()->isObsolete(anchorID) )
3343    {
3344        if ( hasReferencesToAnchorID(anchorID, false) == false )
3345            gIOMediaBSDClientGlobals.getAnchors()->remove(anchorID);
3346    }
3347}
3348
3349UInt32 MinorTable::update( IOMedia *          media,
3350                           UInt32             anchorID,
3351                           IOMediaBSDClient * client,
3352                           char *             slicePath )
3353{
3354    //
3355    // This method searches for the specified anchor ID and slice path pair in
3356    // the minor table and updates it.  An update would be an unusual occasion
3357    // as new anchors are assigned when two media trees are found to share the
3358    // same anchor.  It would occur in one specific circumstance: on the minor
3359    // slot through which a DKIOCFORMAT was issued.  The minor slot would have
3360    // been marked in "open flux", the format would have been issued, then the
3361    // media objects terminated, the minor slot marked obsolete, and the media
3362    // objects republished.  The anchor ID would have one reference, the minor
3363    // slot with the DKIOCFORMAT still outstanding.  AnchorTable::update would
3364    // notice the one reference is orphaned and accept the reuse of the anchor
3365    // ID.  MinorTable::update would notice the orphaned minor slot and update
3366    // it with the new media object and media bsd client object, and clear its
3367    // obsolete state and "open flux" state, once the new media object arrives.
3368    //
3369
3370    UInt32 minorID;
3371    char * minorName;
3372    UInt32 minorNameSize;
3373
3374    // Create a buffer large enough to hold the full name of the minor.
3375
3376    minorNameSize = strlen("disk#");
3377    for (unsigned temp = anchorID; temp >= 10; temp /= 10)  minorNameSize++;
3378    minorNameSize += strlen(slicePath);
3379    minorNameSize += 1;
3380    minorName = IONew(char, minorNameSize);
3381
3382    if ( minorName == 0 )  return kInvalidMinorID;
3383
3384    // Construct a name for the node.
3385
3386    snprintf(minorName, minorNameSize, "disk%d%s", (int) anchorID, slicePath);
3387    assert(strlen(minorName) + 1 == minorNameSize);
3388
3389    // Search for an orphaned slot in the minor table with our minor name.
3390
3391    for ( minorID = 0; minorID < _tableCount; minorID++ )
3392    {
3393        if ( _table[minorID].isAssigned              != false    &&
3394             _table[minorID].isObsolete              != false    &&
3395             _table[minorID].isOrphaned              != false    &&
3396             _table[minorID].anchorID                == anchorID &&
3397             strcmp(_table[minorID].name, minorName) == 0        )  break;
3398    }
3399
3400    IODelete(minorName, char, minorNameSize);
3401
3402    if ( minorID == _tableCount )  return kInvalidMinorID;
3403
3404    // Update the slot and retain the appropriate objects.
3405
3406    _table[minorID].client->release();             // (release client)
3407    _table[minorID].media->release();              // (release media)
3408
3409    _table[minorID].isObsolete = false;            // (update slot)
3410    _table[minorID].isOrphaned = false;
3411    _table[minorID].client     = client;
3412    _table[minorID].media      = media;
3413
3414    _table[minorID].client->retain();              // (retain client)
3415    _table[minorID].media->retain();               // (retain media)
3416
3417    return minorID;
3418}
3419
3420UInt32 MinorTable::locate(IOMedia * media)
3421{
3422    //
3423    // This method searches for the specified media in the minor table and
3424    // returns its ID (or kInvalidMinorID on an error).   It ignores slots
3425    // marked as obsolete.
3426    //
3427
3428    for (UInt32 minorID = 0; minorID < _tableCount; minorID++)
3429    {
3430        if ( _table[minorID].isAssigned != false &&
3431             _table[minorID].isObsolete == false &&
3432             _table[minorID].media      == media )  return minorID;
3433    }
3434
3435    return kInvalidMinorID;
3436}
3437
3438UInt32 MinorTable::getOpenCountForAnchorID(UInt32 anchorID)
3439{
3440    //
3441    // This method obtains a count of opens on the minors associated with the
3442    // specified anchor ID.  A block device open is counted separately from a
3443    // character device open.
3444    //
3445
3446    UInt32 opens = 0;
3447
3448    for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
3449    {
3450        if ( _table[minorID].isAssigned != false    &&
3451             _table[minorID].anchorID   == anchorID )
3452        {
3453            opens += _table[minorID].bdevOpen;
3454            opens += _table[minorID].cdevOpen;
3455        }
3456    }
3457
3458    return opens;
3459}
3460
3461bool MinorTable::hasReferencesToAnchorID(UInt32 anchorID, bool excludeOrphans)
3462{
3463    //
3464    // This method determines whether there are assigned minors in the minor
3465    // table that refer to the specified anchor ID.  It ignores slots marked
3466    // as obsolete and orphaned, unless excludeOrphans is false.
3467    //
3468
3469    for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
3470    {
3471        if ( _table[minorID].isAssigned != false    &&
3472             _table[minorID].anchorID   == anchorID )
3473        {
3474            if ( excludeOrphans             == false )  return true;
3475            if ( _table[minorID].isObsolete == false )  return true;
3476            if ( _table[minorID].isOrphaned == false )  return true;
3477        }
3478    }
3479
3480    return false;
3481}
3482
3483MinorSlot * MinorTable::getMinor(UInt32 minorID)
3484{
3485    //
3486    // Obtain the structure describing the specified minor.
3487    //
3488
3489    if ( minorID < _tableCount && _table[minorID].isAssigned )
3490        return &_table[minorID];
3491    else
3492        return 0;
3493}
3494
3495void MinorTable::obsolete(UInt32 minorID)
3496{
3497    //
3498    // This method obsoletes the specified minor, that is, the slot is marked
3499    // as obsolete and will be removed later via the dkclose function once it
3500    // detects the last close arrive.  Once obsoleted, the minor can be cons-
3501    // idered to be removed, since it will not appear in locate searches.
3502    //
3503
3504    assert(minorID < _tableCount);
3505    assert(_table[minorID].isAssigned);
3506
3507    // Mark the minor as obsolete so that it can be removed from the table as
3508    // soon as the last close arrives (dkclose function's responsibility).
3509
3510    _table[minorID].isObsolete = true;
3511}
3512
3513bool MinorTable::isObsolete(UInt32 minorID)
3514{
3515    //
3516    // Determine whether the specified minor ID is marked as obsolete.
3517    //
3518
3519    assert(minorID < _tableCount);
3520    assert(_table[minorID].isAssigned);
3521
3522    return _table[minorID].isObsolete ? true : false;
3523}
3524
3525// =============================================================================
3526// IOMediaBSDClientGlobals Class
3527
3528static int devsw_add(int index, struct bdevsw * bsw, struct cdevsw * csw)
3529{
3530    for ( index = bdevsw_isfree(index); index != -1; index++, index = bdevsw_isfree(-index) )
3531    {
3532        int bdevsw_index;
3533
3534        bdevsw_index = bdevsw_add(index, bsw);
3535
3536        if (bdevsw_index == index)
3537        {
3538            int cdevsw_index;
3539
3540            cdevsw_index = cdevsw_add_with_bdev(index, csw, index);
3541
3542            if (cdevsw_index == index)
3543            {
3544                break;
3545            }
3546
3547            bdevsw_remove(bdevsw_index, bsw);
3548        }
3549    }
3550
3551    return index;
3552}
3553
3554static int devsw_remove(int index, struct bdevsw * bsw, struct cdevsw * csw)
3555{
3556    index = bdevsw_remove(index, bsw);
3557
3558    if (index != -1)
3559    {
3560        index = cdevsw_remove(index, csw);
3561    }
3562
3563    return index;
3564}
3565
3566IOMediaBSDClientGlobals::IOMediaBSDClientGlobals()
3567{
3568    //
3569    // Initialize the minimal global state.
3570    //
3571
3572    _anchors   = new AnchorTable();
3573    _minors    = new MinorTable();
3574
3575    _majorID   = devsw_add(-1, &bdevswFunctions, &cdevswFunctions);
3576
3577    _openLock  = IOLockAlloc();
3578    _stateLock = IOLockAlloc();
3579///w:start
3580#if !TARGET_OS_EMBEDDED
3581    _assertionCall = thread_call_allocate(dkreadwriteassertion, NULL);
3582    _assertionID   = kIOPMUndefinedDriverAssertionID;
3583    _assertionLock = IOLockAlloc();
3584#endif /* !TARGET_OS_EMBEDDED */
3585///w:stop
3586}
3587
3588IOMediaBSDClientGlobals::~IOMediaBSDClientGlobals()
3589{
3590    //
3591    // Free all of the outstanding global resources.
3592    //
3593
3594///w:start
3595#if !TARGET_OS_EMBEDDED
3596    if ( _assertionCall )               thread_call_free(_assertionCall);
3597    if ( _assertionLock )               IOLockFree(_assertionLock);
3598#endif /* !TARGET_OS_EMBEDDED */
3599///w:stop
3600    if ( _openLock )                    IOLockFree(_openLock);
3601    if ( _stateLock )                   IOLockFree(_stateLock);
3602
3603    if ( _majorID != kInvalidMajorID )  devsw_remove(_majorID, &bdevswFunctions, &cdevswFunctions);
3604
3605    if ( _minors )                      delete _minors;
3606    if ( _anchors )                     delete _anchors;
3607}
3608
3609AnchorTable * IOMediaBSDClientGlobals::getAnchors()
3610{
3611    //
3612    // Obtain the table of anchors.
3613    //
3614
3615    return _anchors;
3616}
3617
3618MinorTable * IOMediaBSDClientGlobals::getMinors()
3619{
3620    //
3621    // Obtain the table of minors.
3622    //
3623
3624    return _minors;
3625}
3626
3627MinorSlot * IOMediaBSDClientGlobals::getMinor(UInt32 minorID)
3628{
3629    //
3630    // Obtain information for the specified minor ID.
3631    //
3632
3633    return _minors->getMinor(minorID);
3634}
3635
3636UInt32 IOMediaBSDClientGlobals::getMajorID()
3637{
3638    //
3639    // Obtain the major ID.
3640    //
3641
3642    return _majorID;
3643}
3644
3645bool IOMediaBSDClientGlobals::isValid()
3646{
3647    //
3648    // Determine whether the minimal global state has been initialized.
3649    //
3650
3651    return ( _anchors                    ) &&
3652           ( _minors                     ) &&
3653           ( _majorID != kInvalidMajorID ) &&
3654///w:start
3655#if !TARGET_OS_EMBEDDED
3656           ( _assertionCall              ) &&
3657           ( _assertionLock              ) &&
3658#endif /* !TARGET_OS_EMBEDDED */
3659///w:stop
3660           ( _openLock                   ) &&
3661           ( _stateLock                  );
3662}
3663
3664void IOMediaBSDClientGlobals::lockOpen()
3665{
3666    //
3667    // Disable access to the opens and closes.
3668    //
3669
3670    IOLockLock(_openLock);
3671}
3672
3673void IOMediaBSDClientGlobals::unlockOpen()
3674{
3675    //
3676    // Enable access to the opens and closes.
3677    //
3678
3679    IOLockUnlock(_openLock);
3680}
3681
3682void IOMediaBSDClientGlobals::lockState()
3683{
3684    //
3685    // Disable access to the global state.
3686    //
3687
3688    IOLockLock(_stateLock);
3689}
3690
3691void IOMediaBSDClientGlobals::unlockState()
3692{
3693    //
3694    // Enable access to the global state.
3695    //
3696
3697    IOLockUnlock(_stateLock);
3698}
3699///w:start
3700#if !TARGET_OS_EMBEDDED
3701thread_call_t IOMediaBSDClientGlobals::getAssertionCall()
3702{
3703    return _assertionCall;
3704}
3705
3706IOPMDriverAssertionID IOMediaBSDClientGlobals::getAssertionID()
3707{
3708    return _assertionID;
3709}
3710
3711void IOMediaBSDClientGlobals::setAssertionID(IOPMDriverAssertionID assertionID)
3712{
3713    _assertionID = assertionID;
3714}
3715
3716AbsoluteTime IOMediaBSDClientGlobals::getAssertionTime()
3717{
3718    return _assertionTime;
3719}
3720
3721void IOMediaBSDClientGlobals::setAssertionTime(AbsoluteTime assertionTime)
3722{
3723    _assertionTime = assertionTime;
3724}
3725
3726void IOMediaBSDClientGlobals::lockAssertion()
3727{
3728    IOLockLock(_assertionLock);
3729}
3730
3731void IOMediaBSDClientGlobals::unlockAssertion()
3732{
3733    IOLockUnlock(_assertionLock);
3734}
3735#endif /* !TARGET_OS_EMBEDDED */
3736///w:stop
3737