1/*
2 * Copyright (c) 1998-2013 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    uint8_t       reserved0064[8];
699} dk_unmap_32_t;
700
701typedef struct
702{
703    user64_addr_t extents;
704    uint32_t      extentsCount;
705
706    uint8_t       reserved0096[4];
707} dk_unmap_64_t;
708
709static IOStorageAccess DK_ADD_ACCESS(IOStorageAccess a1, IOStorageAccess a2)
710{
711    static UInt8 table[4][4] =
712    {            /* Rea, Wri, R|S, W|S */
713        /* Rea */ { 000, 001, 002, 003 },
714        /* Wri */ { 001, 001, 001, 001 },
715        /* R|S */ { 002, 001, 002, 003 },
716        /* W|S */ { 003, 001, 003, 003 }
717    };
718
719    if ( a1 == kIOStorageAccessNone )  return a2;
720    if ( a2 == kIOStorageAccessNone )  return a1;
721
722    a1 = (a1 - 1) >> 1;
723    a2 = (a2 - 1) >> 1;
724
725    if ( a1 > 003 )  return kIOStorageAccessNone;
726    if ( a2 > 003 )  return kIOStorageAccessNone;
727
728    return (table[a1][a2] << 1) + 1;
729}
730
731static bool DKIOC_IS_RESERVED(caddr_t data, uint32_t reserved)
732{
733    UInt32 index;
734
735    for ( index = 0; index < sizeof(reserved) * 8; index++, reserved >>= 1 )
736    {
737        if ( (reserved & 1) )
738        {
739            if ( data[index] )  return true;
740        }
741    }
742
743    return false;
744}
745
746UInt64 _IOMediaBSDClientGetThrottleMask(IOMedia * media)
747{
748    UInt64 mask;
749
750    mask = 0;
751
752    if ( media )
753    {
754        int error;
755
756        error = EAGAIN;
757
758        while ( error )
759        {
760            // Iterate through IOBlockStorageDevice objects.
761
762            IORegistryIterator * devices;
763
764            error = 0;
765
766            mask = 0;
767
768            devices = IORegistryIterator::iterateOver( media, gIOServicePlane, kIORegistryIterateParents );
769
770            if ( devices )
771            {
772                IORegistryEntry * device;
773
774                device = devices->getNextObjectRecursive( );
775
776                while ( device )
777                {
778                    if ( OSDynamicCast( IOBlockStorageDevice, device ) )
779                    {
780                        // Iterate through IOMedia objects.
781
782                        IORegistryIterator * services;
783
784                        services = IORegistryIterator::iterateOver( device, gIOServicePlane );
785
786                        if ( services )
787                        {
788                            IORegistryEntry * service;
789
790                            service = services->getNextObjectRecursive( );
791
792                            while ( service )
793                            {
794                                if ( OSDynamicCast( IOMedia, service ) )
795                                {
796                                    // Obtain the BSD Unit property.
797
798                                    OSNumber * unit;
799
800                                    unit = OSDynamicCast( OSNumber, service->getProperty( kIOBSDUnitKey ) );
801
802                                    if ( unit )
803                                    {
804                                        mask |= 1 << ( unit->unsigned32BitValue( ) % 64 );
805                                    }
806                                }
807
808                                service = services->getNextObjectRecursive( );
809                            }
810
811                            if ( services->isValid( ) == false )
812                            {
813                                error = EAGAIN;
814                            }
815
816                            services->release( );
817                        }
818
819///w:start
820                        OSNumber * number;
821
822                        number = OSDynamicCast( OSNumber, device->getProperty( "throttle-unit" ) );
823
824                        if ( number )
825                        {
826                            OSDictionary * dictionary;
827
828                            dictionary = IOService::serviceMatching( kIOMediaClass );
829
830                            if ( dictionary )
831                            {
832                                OSIterator * iterator;
833
834                                dictionary->setObject( kIOBSDUnitKey, number );
835
836                                iterator = IOService::getMatchingServices( dictionary );
837
838                                if ( iterator )
839                                {
840                                    OSObject * object;
841
842                                    object = iterator->getNextObject( );
843
844                                    if ( object )
845                                    {
846                                        mask |= _IOMediaBSDClientGetThrottleMask( ( IOMedia * ) object );
847                                    }
848
849                                    iterator->release( );
850                                }
851
852                                dictionary->release( );
853                            }
854                        }
855///w:stop
856                        devices->exitEntry( );
857                    }
858
859                    device = devices->getNextObjectRecursive( );
860                }
861
862                if ( devices->isValid( ) == false )
863                {
864                    error = EAGAIN;
865                }
866
867                devices->release( );
868            }
869        }
870    }
871
872    return mask;
873}
874
875int dkopen(dev_t dev, int flags, int devtype, proc_t /* proc */)
876{
877    //
878    // dkopen opens the device (called on each open).
879    //
880
881    IOStorageAccess access;
882    int             error;
883    IOStorageAccess level;
884    IOStorageAccess levelOut;
885    IOMedia *       media;
886    MinorSlot *     minor;
887
888    assert(S_ISBLK(devtype) || S_ISCHR(devtype));
889
890    gIOMediaBSDClientGlobals.lockOpen();    // (disable access to opens, closes)
891    gIOMediaBSDClientGlobals.lockState();   // (disable access to state, tables)
892
893    access  = kIOStorageAccessReader;
894    access |= (flags &   FWRITE) ? kIOStorageAccessReaderWriter  : 0;
895    access |= (flags & O_SHLOCK) ? kIOStorageAccessSharedLock    : 0;
896    access |= (flags & O_EXLOCK) ? kIOStorageAccessExclusiveLock : 0;
897
898    error = 0;
899    media = 0;
900    minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
901
902    //
903    // Process the open.
904    //
905
906    if ( minor == 0 )                                       // (is minor valid?)
907    {
908        error = ENXIO;
909    }
910    else if ( minor->isOrphaned )                         // (is minor in flux?)
911    {
912        error = EBUSY;
913    }
914    else
915    {
916///w:start
917#ifdef __LP64__
918        static int root = 0;
919
920        if ( root == 0 )
921        {
922            root = 1;
923
924            if ( minor->media->isWritable() )
925            {
926                access |= kIOStorageAccessReaderWriter;
927            }
928        }
929#endif /* __LP64__ */
930///w:stop
931        level    = DK_ADD_ACCESS(minor->bdevOpenLevel, minor->cdevOpenLevel);
932        levelOut = DK_ADD_ACCESS(level, access);
933
934        if ( levelOut == kIOStorageAccessNone )            // (is access valid?)
935        {
936            error = EBUSY;
937        }
938        else if ( (flags & FWRITE) )                    // (is client a writer?)
939        {
940            if ( minor->media->isWritable() == false )
941            {
942                error = EACCES;
943            }
944        }
945    }
946
947    if ( error == 0 )                                                   // (go?)
948    {
949        IOStorageAccess wasOpenLevel;
950
951        if ( S_ISBLK(devtype) )                                // (update state)
952        {
953            minor->bdevOpen++;
954            wasOpenLevel = minor->bdevOpenLevel;
955            minor->bdevOpenLevel = DK_ADD_ACCESS(wasOpenLevel, access);
956        }
957        else
958        {
959            minor->cdevOpen++;
960            wasOpenLevel = minor->cdevOpenLevel;
961            minor->cdevOpenLevel = DK_ADD_ACCESS(wasOpenLevel, access);
962        }
963
964        gIOMediaBSDClientGlobals.unlockState();     // (enable access to tables)
965
966        if ( level != levelOut )                        // (issue open/upgrade?)
967        {
968            bool success;
969
970            media = minor->media;
971            minor->media->retain();
972
973            success = minor->media->open(minor->client, 0, levelOut);    // (go)
974
975            if ( success == false )
976            {
977                gIOMediaBSDClientGlobals.lockState();        // (disable access)
978
979                if ( S_ISBLK(devtype) )                          // (undo state)
980                {
981                    minor->bdevOpen--;
982                    minor->bdevOpenLevel = wasOpenLevel;
983                }
984                else
985                {
986                    minor->cdevOpen--;
987                    minor->cdevOpenLevel = wasOpenLevel;
988                }
989
990                assert(minor->isOrphaned == false);
991
992                if ( !minor->bdevOpen && !minor->cdevOpen && minor->isObsolete )
993                {
994                    gIOMediaBSDClientGlobals.getMinors()->remove(getminor(dev));
995                }
996
997                gIOMediaBSDClientGlobals.unlockState();       // (enable access)
998
999                error = EBUSY;
1000            }
1001        }
1002    }
1003    else
1004    {
1005        gIOMediaBSDClientGlobals.unlockState();     // (enable access to tables)
1006    }
1007
1008    gIOMediaBSDClientGlobals.unlockOpen();   // (enable access to opens, closes)
1009
1010    //
1011    // Wait until I/O Kit has finished to attempt to match storage drivers
1012    // or terminate storage drivers, should the media object have been re-
1013    // registered or its storage driver been terminated as a result of the
1014    // open.
1015    //
1016
1017    if ( media )
1018    {
1019        media->waitQuiet();
1020        media->release();
1021    }
1022
1023    return error;
1024}
1025
1026int dkclose(dev_t dev, int /* flags */, int devtype, proc_t /* proc */)
1027{
1028    //
1029    // dkclose closes the device (called on last close).
1030    //
1031
1032    IOStorageAccess level;
1033    IOStorageAccess levelOut;
1034    IOMedia *       media;
1035    MinorSlot *     minor;
1036
1037    assert(S_ISBLK(devtype) || S_ISCHR(devtype));
1038
1039    gIOMediaBSDClientGlobals.lockOpen();    // (disable access to opens, closes)
1040    gIOMediaBSDClientGlobals.lockState();   // (disable access to state, tables)
1041
1042    media = 0;
1043    minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
1044
1045    level = DK_ADD_ACCESS(minor->bdevOpenLevel, minor->cdevOpenLevel);
1046
1047    if ( S_ISBLK(devtype) )                                    // (update state)
1048    {
1049        minor->bdevBlockSize = minor->media->getPreferredBlockSize();
1050        minor->bdevOpen      = 0;
1051        minor->bdevOpenLevel = kIOStorageAccessNone;
1052    }
1053    else
1054    {
1055        minor->cdevOpen      = 0;
1056        minor->cdevOpenLevel = kIOStorageAccessNone;
1057#if TARGET_OS_EMBEDDED
1058        minor->cdevOptions   = 0;
1059#endif /* TARGET_OS_EMBEDDED */
1060    }
1061
1062    levelOut = DK_ADD_ACCESS(minor->bdevOpenLevel, minor->cdevOpenLevel);
1063
1064    if ( minor->isOrphaned )                              // (is minor in flux?)
1065    {
1066        //
1067        // We have determined that the specified minor is in "open flux".  This
1068        // means we are in a state where the media object has been closed, only
1069        // the device node is still open.  This happens to the minor subsequent
1070        // to a DKIOCEJECT ioctl -- this close resets the flux state to normal.
1071        //
1072
1073        minor->isOrphaned = false;
1074
1075        // If this minor is marked as obsolete, then we've already received the
1076        // media's termination notification, but the minor is yet to be removed
1077        // from the table -- remove it now.
1078
1079        assert(minor->bdevOpen == 0);
1080        assert(minor->cdevOpen == 0);
1081
1082        if ( minor->isObsolete )
1083        {
1084            gIOMediaBSDClientGlobals.getMinors()->remove(getminor(dev));
1085        }
1086
1087        gIOMediaBSDClientGlobals.unlockState();     // (enable access to tables)
1088    }
1089    else if ( !minor->bdevOpen && !minor->cdevOpen )
1090    {
1091        //
1092        // We communicate the close down to the media object once all opens are
1093        // gone, on both the block and character device nodes.
1094        //
1095
1096        IOMediaBSDClient * client;
1097
1098        client = minor->client;
1099        minor->client->retain();
1100
1101        media = minor->media;
1102        minor->media->retain();
1103
1104        // If this minor is marked as obsolete, then we've already received the
1105        // media's termination notification, but the minor is yet to be removed
1106        // from the table -- remove it now.
1107
1108        if ( minor->isObsolete )
1109        {
1110            gIOMediaBSDClientGlobals.getMinors()->remove(getminor(dev));
1111        }
1112
1113        gIOMediaBSDClientGlobals.unlockState();     // (enable access to tables)
1114
1115        media->close(client);                                            // (go)
1116
1117        client->release();
1118    }
1119    else if ( level != levelOut )
1120    {
1121        //
1122        // We communicate the downgrade down to the media object.
1123        //
1124
1125        media = minor->media;
1126        minor->media->retain();
1127
1128        gIOMediaBSDClientGlobals.unlockState();     // (enable access to tables)
1129
1130        minor->media->open(minor->client, 0, levelOut);                  // (go)
1131    }
1132    else
1133    {
1134        gIOMediaBSDClientGlobals.unlockState();     // (enable access to tables)
1135    }
1136
1137    gIOMediaBSDClientGlobals.unlockOpen();   // (enable access to opens, closes)
1138
1139    //
1140    // Wait until I/O Kit has finished to attempt to match storage drivers,
1141    // should the media object have been re-registered as a result of this
1142    // close.
1143    //
1144
1145    if ( media )
1146    {
1147        media->waitQuiet();
1148        media->release();
1149    }
1150
1151    return 0;
1152}
1153
1154int dkread(dev_t dev, uio_t uio, int /* flags */)
1155{
1156    //
1157    // dkread reads data from a device.
1158    //
1159
1160    struct dio dio = { dev, uio };
1161
1162    return dkreadwrite(&dio, DKRTYPE_DIO);
1163}
1164
1165int dkwrite(dev_t dev, uio_t uio, int /* flags */)
1166{
1167    //
1168    // dkwrite writes data to a device.
1169    //
1170
1171    struct dio dio = { dev, uio };
1172
1173    return dkreadwrite(&dio, DKRTYPE_DIO);
1174}
1175
1176void dkstrategy(buf_t bp)
1177{
1178    //
1179    // dkstrategy starts an asynchronous read or write operation.  It returns
1180    // to the caller as soon as the operation is queued, and completes it via
1181    // the buf_biodone function.
1182    //
1183
1184    dkreadwrite(bp, DKRTYPE_BUF);
1185}
1186
1187int dkioctl(dev_t dev, u_long cmd, caddr_t data, int flags, proc_t proc)
1188{
1189    //
1190    // dkioctl performs operations other than a read or write.
1191    //
1192
1193    int         error = 0;
1194    MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
1195
1196    if ( minor->isOrphaned )  return EBADF;               // (is minor in flux?)
1197
1198    //
1199    // Process the ioctl.
1200    //
1201
1202    switch ( cmd )
1203    {
1204        case DKIOCGETBLOCKSIZE:                                  // (uint32_t *)
1205        {
1206            //
1207            // This ioctl returns the preferred block size of the media object.
1208            //
1209
1210            *(uint32_t *)data = minor->media->getPreferredBlockSize();
1211
1212        } break;
1213
1214#ifndef __LP64__
1215        case DKIOCGETBLOCKCOUNT32:                               // (uint32_t *)
1216        {
1217            //
1218            // This ioctl returns the size of the media object in blocks.  The
1219            // implied block size is returned by DKIOCGETBLOCKSIZE.
1220            //
1221
1222            if ( minor->media->getPreferredBlockSize() )
1223                *(uint32_t *)data = ( minor->media->getSize()               /
1224                                      minor->media->getPreferredBlockSize() );
1225            else
1226                *(uint32_t *)data = 0;
1227
1228        } break;
1229#endif /* !__LP64__ */
1230
1231        case DKIOCGETBLOCKCOUNT:                                 // (uint64_t *)
1232        {
1233            //
1234            // This ioctl returns the size of the media object in blocks.  The
1235            // implied block size is returned by DKIOCGETBLOCKSIZE.
1236            //
1237
1238            if ( minor->media->getPreferredBlockSize() )
1239                *(uint64_t *)data = ( minor->media->getSize()               /
1240                                      minor->media->getPreferredBlockSize() );
1241            else
1242                *(uint64_t *)data = 0;
1243
1244        } break;
1245
1246        case DKIOCGETMAXBLOCKCOUNTREAD:                          // (uint64_t *)
1247        {
1248            //
1249            // This ioctl returns the maximum block count for reads.
1250            //
1251
1252            OSNumber * number = OSDynamicCast(
1253                         /* class  */ OSNumber,
1254                         /* object */ minor->media->getProperty(
1255                                 /* key   */ kIOMaximumBlockCountReadKey,
1256                                 /* plane */ gIOServicePlane ) );
1257            if ( number )
1258                *(uint64_t *)data = number->unsigned64BitValue();
1259            else
1260                *(uint64_t *)data = 0;
1261
1262        } break;
1263
1264        case DKIOCGETMAXBLOCKCOUNTWRITE:                         // (uint64_t *)
1265        {
1266            //
1267            // This ioctl returns the maximum block count for writes.
1268            //
1269
1270            OSNumber * number = OSDynamicCast(
1271                         /* class  */ OSNumber,
1272                         /* object */ minor->media->getProperty(
1273                                 /* key   */ kIOMaximumBlockCountWriteKey,
1274                                 /* plane */ gIOServicePlane ) );
1275            if ( number )
1276                *(uint64_t *)data = number->unsigned64BitValue();
1277            else
1278                *(uint64_t *)data = 0;
1279
1280        } break;
1281
1282        case DKIOCGETMAXBYTECOUNTREAD:                           // (uint64_t *)
1283        {
1284            //
1285            // This ioctl returns the maximum byte count for reads.
1286            //
1287
1288            OSNumber * number = OSDynamicCast(
1289                         /* class  */ OSNumber,
1290                         /* object */ minor->media->getProperty(
1291                                 /* key   */ kIOMaximumByteCountReadKey,
1292                                 /* plane */ gIOServicePlane ) );
1293            if ( number )
1294                *(uint64_t *)data = number->unsigned64BitValue();
1295            else
1296                *(uint64_t *)data = 0;
1297
1298        } break;
1299
1300        case DKIOCGETMAXBYTECOUNTWRITE:                          // (uint64_t *)
1301        {
1302            //
1303            // This ioctl returns the maximum byte count for writes.
1304            //
1305
1306            OSNumber * number = OSDynamicCast(
1307                         /* class  */ OSNumber,
1308                         /* object */ minor->media->getProperty(
1309                                 /* key   */ kIOMaximumByteCountWriteKey,
1310                                 /* plane */ gIOServicePlane ) );
1311            if ( number )
1312                *(uint64_t *)data = number->unsigned64BitValue();
1313            else
1314                *(uint64_t *)data = 0;
1315
1316        } break;
1317
1318        case DKIOCGETMAXSEGMENTCOUNTREAD:                        // (uint64_t *)
1319        {
1320            //
1321            // This ioctl returns the maximum segment count for reads.
1322            //
1323
1324            OSNumber * number = OSDynamicCast(
1325                         /* class  */ OSNumber,
1326                         /* object */ minor->media->getProperty(
1327                                 /* key   */ kIOMaximumSegmentCountReadKey,
1328                                 /* plane */ gIOServicePlane ) );
1329            if ( number )
1330                *(uint64_t *)data = number->unsigned64BitValue();
1331            else
1332                *(uint64_t *)data = 0;
1333
1334        } break;
1335
1336        case DKIOCGETMAXSEGMENTCOUNTWRITE:                       // (uint64_t *)
1337        {
1338            //
1339            // This ioctl returns the maximum segment count for writes.
1340            //
1341
1342            OSNumber * number = OSDynamicCast(
1343                         /* class  */ OSNumber,
1344                         /* object */ minor->media->getProperty(
1345                                 /* key   */ kIOMaximumSegmentCountWriteKey,
1346                                 /* plane */ gIOServicePlane ) );
1347            if ( number )
1348                *(uint64_t *)data = number->unsigned64BitValue();
1349            else
1350                *(uint64_t *)data = 0;
1351
1352        } break;
1353
1354        case DKIOCGETMAXSEGMENTBYTECOUNTREAD:                    // (uint64_t *)
1355        {
1356            //
1357            // This ioctl returns the maximum segment byte count for reads.
1358            //
1359
1360            OSNumber * number = OSDynamicCast(
1361                         /* class  */ OSNumber,
1362                         /* object */ minor->media->getProperty(
1363                                 /* key   */ kIOMaximumSegmentByteCountReadKey,
1364                                 /* plane */ gIOServicePlane ) );
1365            if ( number )
1366                *(uint64_t *)data = number->unsigned64BitValue();
1367            else
1368                *(uint64_t *)data = 0;
1369
1370        } break;
1371
1372        case DKIOCGETMAXSEGMENTBYTECOUNTWRITE:                   // (uint64_t *)
1373        {
1374            //
1375            // This ioctl returns the maximum segment byte count for writes.
1376            //
1377
1378            OSNumber * number = OSDynamicCast(
1379                         /* class  */ OSNumber,
1380                         /* object */ minor->media->getProperty(
1381                                 /* key   */ kIOMaximumSegmentByteCountWriteKey,
1382                                 /* plane */ gIOServicePlane ) );
1383            if ( number )
1384                *(uint64_t *)data = number->unsigned64BitValue();
1385            else
1386                *(uint64_t *)data = 0;
1387
1388        } break;
1389
1390        case DKIOCGETMINSEGMENTALIGNMENTBYTECOUNT:               // (uint64_t *)
1391        {
1392            //
1393            // This ioctl returns the minimum segment alignment in bytes.
1394            //
1395
1396            OSNumber * number = OSDynamicCast(
1397                         /* class  */ OSNumber,
1398                         /* object */ minor->media->getProperty(
1399                                 /* key   */ kIOMinimumSegmentAlignmentByteCountKey,
1400                                 /* plane */ gIOServicePlane ) );
1401            if ( number )
1402                *(uint64_t *)data = number->unsigned64BitValue();
1403            else
1404                *(uint64_t *)data = 0;
1405
1406        } break;
1407
1408        case DKIOCGETMAXSEGMENTADDRESSABLEBITCOUNT:              // (uint64_t *)
1409        {
1410            //
1411            // This ioctl returns the maximum segment width in bits.
1412            //
1413
1414            OSNumber * number = OSDynamicCast(
1415                         /* class  */ OSNumber,
1416                         /* object */ minor->media->getProperty(
1417                                 /* key   */ kIOMaximumSegmentAddressableBitCountKey,
1418                                 /* plane */ gIOServicePlane ) );
1419            if ( number )
1420                *(uint64_t *)data = number->unsigned64BitValue();
1421            else
1422                *(uint64_t *)data = 0;
1423
1424        } break;
1425
1426        case DKIOCGETPHYSICALBLOCKSIZE:                          // (uint32_t *)
1427        {
1428            //
1429            // This ioctl returns the preferred block size of the device.
1430            //
1431
1432            OSNumber * number = OSDynamicCast(
1433                         /* class  */ OSNumber,
1434                         /* object */ minor->media->getProperty(
1435                                 /* key   */ kIOPropertyPhysicalBlockSizeKey,
1436                                 /* plane */ gIOServicePlane ) );
1437            if ( number )
1438                *(uint32_t *)data = number->unsigned32BitValue();
1439            else
1440                *(uint32_t *)data = minor->media->getPreferredBlockSize();
1441
1442        } break;
1443
1444        case DKIOCGETCOMMANDPOOLSIZE:                            // (uint32_t *)
1445        {
1446            //
1447            // This ioctl returns the maximum queue depth of the device.
1448            //
1449
1450            OSNumber * number = OSDynamicCast(
1451                         /* class  */ OSNumber,
1452                         /* object */ minor->media->getProperty(
1453                                 /* key   */ kIOCommandPoolSizeKey,
1454                                 /* plane */ gIOServicePlane ) );
1455            if ( number )
1456                *(uint32_t *)data = number->unsigned32BitValue();
1457            else
1458                *(uint32_t *)data = 0;
1459
1460        } break;
1461
1462        case DKIOCISFORMATTED:                                   // (uint32_t *)
1463        {
1464            //
1465            // This ioctl returns truth if the media object is formatted.
1466            //
1467
1468            *(uint32_t *)data = minor->media->isFormatted();
1469
1470        } break;
1471
1472        case DKIOCISWRITABLE:                                    // (uint32_t *)
1473        {
1474            //
1475            // This ioctl returns truth if the media object is writable.
1476            //
1477
1478            *(uint32_t *)data = minor->media->isWritable();
1479
1480        } break;
1481
1482        case DKIOCEJECT:                                               // (void)
1483        {
1484            //
1485            // This ioctl asks that the media object be ejected from the device.
1486            //
1487
1488            IOMediaBSDClient *     client;
1489            IOBlockStorageDriver * driver;
1490            MinorTable *           minors;
1491            IOReturn               status;
1492
1493            client = minor->client;
1494            driver = (IOBlockStorageDriver *) minor->media->getProvider();
1495            driver = OSDynamicCast(IOBlockStorageDriver, driver);
1496            minors = gIOMediaBSDClientGlobals.getMinors();
1497
1498            // Determine whether this media has an IOBlockStorageDriver parent.
1499
1500            if ( driver == 0 )  { error = ENOTTY;  break; }
1501
1502            // Disable access to opens, closes, tables.
1503
1504            gIOMediaBSDClientGlobals.lockOpen();
1505            gIOMediaBSDClientGlobals.lockState();
1506
1507            // Determine whether there are other opens on the device nodes that
1508            // are associated with this anchor -- the one valid open is the one
1509            // that issued this eject.  If all is well, we then attempt to open
1510            // the block storage driver to make the ejection request.
1511
1512            if ( minors->getOpenCountForAnchorID(minor->anchorID) == 1 &&
1513                 driver->open(client, 0, kIOStorageAccessReaderWriter) )
1514            {
1515                // Mark the minor as being in "open flux".  This means we are in
1516                // a state where the media object has been closed but the device
1517                // node is still open; we must reject all future accesses to the
1518                // device node until it is closed.  Note that we do this both on
1519                // success and failure of the ejection call.
1520
1521                minor->isOrphaned = true;
1522
1523                // Enable access to opens, closes, tables.
1524
1525                gIOMediaBSDClientGlobals.unlockState();
1526                gIOMediaBSDClientGlobals.unlockOpen();
1527
1528                // Close the media object before the ejection request is made.
1529
1530                minor->media->close(client);
1531
1532                // Retain the media's BSD client object, as it is about
1533                // to be terminated, and we still need it for the close.
1534
1535                client->retain();
1536
1537                // Eject the media from the drive.
1538
1539                status = driver->ejectMedia();
1540                error  = driver->errnoFromReturn(status);
1541
1542                // Close the block storage driver.
1543
1544                driver->close(client);
1545
1546                // Release the media's BSD client object.
1547
1548                client->release();
1549            }
1550            else
1551            {
1552                error = EBUSY;
1553
1554                // Enable access to opens, closes, tables.
1555
1556                gIOMediaBSDClientGlobals.unlockState();
1557                gIOMediaBSDClientGlobals.unlockOpen();
1558            }
1559
1560        } break;
1561
1562        case DKIOCFORMAT:                            // (dk_format_capacity_t *)
1563        {
1564            //
1565            // This ioctl asks that the media object be formatted.
1566            //
1567
1568            IOMediaBSDClient *     client;
1569            IOBlockStorageDriver * driver;
1570            MinorTable *           minors;
1571            dk_format_capacity_t * request;
1572            IOReturn               status;
1573
1574            client  = minor->client;
1575            driver  = (IOBlockStorageDriver *) minor->media->getProvider();
1576            driver  = OSDynamicCast(IOBlockStorageDriver, driver);
1577            minors  = gIOMediaBSDClientGlobals.getMinors();
1578            request = (dk_format_capacity_t *) data;
1579
1580            if ( DKIOC_IS_RESERVED(data, 0xF000) )  { error = EINVAL;  break; }
1581
1582            // Determine whether this media has an IOBlockStorageDriver parent.
1583
1584            if ( driver == 0 )  { error = ENOTTY;  break; }
1585
1586            // Disable access to opens, closes, tables.
1587
1588            gIOMediaBSDClientGlobals.lockOpen();
1589            gIOMediaBSDClientGlobals.lockState();
1590
1591            // Determine whether there are other opens on the device nodes that
1592            // are associated with this anchor -- the one valid open is the one
1593            // that issued the format.  If all is well, we then attempt to open
1594            // the block storage driver to make the formatting request.
1595
1596            if ( minors->getOpenCountForAnchorID(minor->anchorID) == 1 &&
1597                 driver->open(client, 0, kIOStorageAccessReaderWriter) )
1598            {
1599                UInt64 capacity = request->blockCount * request->blockSize;
1600
1601                // Mark the minor as being in "open flux".  This means we are in
1602                // a state where the media object has been closed but the device
1603                // node is still open; we must reject all future accesses to the
1604                // device node until it is closed.  Note that we do this both on
1605                // success and failure of the formatting call.
1606
1607                minor->isOrphaned = true;
1608
1609                // Enable access to opens, closes, tables.
1610
1611                gIOMediaBSDClientGlobals.unlockState();
1612                gIOMediaBSDClientGlobals.unlockOpen();
1613
1614                // Close the media object before the formatting request is made.
1615
1616                minor->media->close(client);
1617
1618                // Retain the media's BSD client object, as it is about
1619                // to be terminated, and we still need it for the close.
1620
1621                client->retain();
1622
1623                // Format the media in the drive.
1624
1625                status = driver->formatMedia(capacity);
1626                error  = driver->errnoFromReturn(status);
1627
1628                // Wait until I/O Kit has finished to attempt to match storage
1629                // drivers, since the media object will have been re-published.
1630                // This shall ensure the new IOMediaBSDClient reconnects prior
1631                // to our return from DKIOCFORMAT.  Note that we still recover
1632                // correctly in case the media object doesn't get re-published,
1633                // as though an ejection had taken place.
1634
1635                driver->waitQuiet();
1636
1637                // Close the block storage driver.
1638
1639                driver->close(client);
1640
1641                // Release the media's BSD client object.
1642
1643                client->release();
1644            }
1645            else
1646            {
1647                error = EBUSY;
1648
1649                // Enable access to opens, closes, tables.
1650
1651                gIOMediaBSDClientGlobals.unlockState();
1652                gIOMediaBSDClientGlobals.unlockOpen();
1653            }
1654
1655        } break;
1656
1657        case DKIOCGETFORMATCAPACITIES:             // (dk_format_capacities_t *)
1658        {
1659            //
1660            // This ioctl returns the feasible format capacities for this media
1661            // object.
1662            //
1663
1664            UInt64                      blockSize;
1665            UInt64 *                    capacities;
1666            UInt32                      capacitiesCount;
1667            UInt32                      capacitiesMaxCount;
1668            IOBlockStorageDriver *      driver;
1669            dk_format_capacities_64_t   request;
1670            dk_format_capacities_32_t * request32;
1671            dk_format_capacities_64_t * request64;
1672
1673            driver    = (IOBlockStorageDriver *) minor->media->getProvider();
1674            driver    = OSDynamicCast(IOBlockStorageDriver, driver);
1675            request32 = (dk_format_capacities_32_t *) data;
1676            request64 = (dk_format_capacities_64_t *) data;
1677
1678            if ( proc_is64bit(proc) )
1679            {
1680                if ( DKIOC_IS_RESERVED(data, 0xF000) )  { error = EINVAL;  break; }
1681
1682                request.capacities      = request64->capacities;
1683                request.capacitiesCount = request64->capacitiesCount;
1684            }
1685            else
1686            {
1687                if ( DKIOC_IS_RESERVED(data, 0xFF00) )  { error = EINVAL;  break; }
1688
1689                request.capacities      = request32->capacities;
1690                request.capacitiesCount = request32->capacitiesCount;
1691            }
1692
1693            // Determine whether this media has an IOBlockStorageDriver parent.
1694
1695            if ( driver == 0 )  { error = ENOTTY;  break; }
1696
1697            // Obtain the format capacities list from the block storage driver.
1698
1699            capacitiesCount    = request.capacitiesCount;
1700            capacitiesMaxCount = driver->getFormatCapacities(0, 0);
1701
1702            if ( capacitiesCount )
1703            {
1704                if ( request.capacities == 0 )  { error = EINVAL;  break; }
1705
1706                capacitiesCount = min(capacitiesCount, capacitiesMaxCount);
1707                capacities      = IONew(UInt64, capacitiesCount);
1708
1709                if ( capacities == 0 )  { error = ENOMEM;  break; }
1710
1711                driver->getFormatCapacities(capacities, capacitiesCount);
1712
1713                blockSize = minor->media->getPreferredBlockSize();
1714                if ( blockSize == 0 )  blockSize = DEV_BSIZE;
1715
1716                // Construct the format capacities list for client consumption.
1717
1718                for ( UInt32 index = 0; index < capacitiesCount; index++ )
1719                {
1720                    dk_format_capacity_t capacity = { 0 };
1721
1722                    capacity.blockCount = capacities[index] / blockSize;
1723                    capacity.blockSize  = blockSize;
1724
1725                    if ( proc == kernproc )
1726                    {
1727                        bcopy( /* src */ &capacity,
1728                               /* dst */ (void *) (request.capacities + index * sizeof(dk_format_capacity_t)),
1729                               /* n   */ sizeof(dk_format_capacity_t) );
1730                    }
1731                    else
1732                    {
1733                        error = copyout( /* kaddr */ &capacity,
1734                                         /* uaddr */ request.capacities + index * sizeof(dk_format_capacity_t),
1735                                         /* len   */ sizeof(dk_format_capacity_t) );
1736                    }
1737
1738                    if ( error )  break;
1739                }
1740
1741                IODelete(capacities, UInt64, capacitiesCount);
1742
1743                if ( capacitiesCount < capacitiesMaxCount )  { error = E2BIG; }
1744            }
1745
1746            if ( proc_is64bit(proc) )
1747            {
1748                request64->capacitiesCount = request.capacitiesCount;
1749            }
1750            else
1751            {
1752                request32->capacitiesCount = request.capacitiesCount;
1753            }
1754
1755        } break;
1756
1757        case DKIOCSYNCHRONIZECACHE:                                    // (void)
1758        {
1759            //
1760            // This ioctl asks that the media object be flushed onto the device.
1761            //
1762
1763            IOReturn status;
1764
1765            // Flush the media onto the drive.
1766
1767            status = minor->media->synchronizeCache(minor->client);
1768            error  = minor->media->errnoFromReturn(status);
1769
1770        } break;
1771
1772        case DKIOCUNMAP:                                         // (dk_unmap_t)
1773        {
1774            //
1775            // This ioctl asks that the media object delete unused data.
1776            //
1777
1778            IOStorageExtent * extents;
1779            dk_unmap_64_t     request;
1780            dk_unmap_32_t *   request32;
1781            dk_unmap_64_t *   request64;
1782            IOReturn          status;
1783
1784            assert(sizeof(dk_extent_t) == sizeof(IOStorageExtent));
1785
1786            request32 = (dk_unmap_32_t *) data;
1787            request64 = (dk_unmap_64_t *) data;
1788
1789            if ( proc_is64bit(proc) )
1790            {
1791                if ( DKIOC_IS_RESERVED(data, 0xF000) )  { error = EINVAL;  break; }
1792
1793                request.extents      = request64->extents;
1794                request.extentsCount = request64->extentsCount;
1795            }
1796            else
1797            {
1798                if ( DKIOC_IS_RESERVED(data, 0xFF00) )  { error = EINVAL;  break; }
1799
1800                request.extents      = request32->extents;
1801                request.extentsCount = request32->extentsCount;
1802            }
1803
1804            // Delete unused data from the media.
1805
1806            if ( request.extents == 0 )  { error = EINVAL;  break; }
1807
1808            extents = IONew(IOStorageExtent, request.extentsCount);
1809
1810            if ( extents == 0 )  { error = ENOMEM;  break; }
1811
1812            if ( proc == kernproc )
1813            {
1814                bcopy( /* src */ (void *) request.extents,
1815                       /* dst */ extents,
1816                       /* n   */ request.extentsCount * sizeof(IOStorageExtent) );
1817            }
1818            else
1819            {
1820                error = copyin( /* uaddr */ request.extents,
1821                                /* kaddr */ extents,
1822                                /* len   */ request.extentsCount * sizeof(IOStorageExtent) );
1823            }
1824
1825            if ( error == 0 )
1826            {
1827                status = minor->media->unmap( /* client       */ minor->client,
1828                                              /* extents      */ extents,
1829                                              /* extentsCount */ request.extentsCount );
1830
1831                error = minor->media->errnoFromReturn(status);
1832            }
1833
1834            IODelete(extents, IOStorageExtent, request.extentsCount);
1835
1836        } break;
1837
1838        case DKIOCREQUESTIDLE:                                         // (void)
1839        {
1840            //
1841            // This ioctl asks that the device enter an idle state.
1842            //
1843
1844            IOBlockStorageDriver * driver;
1845            IOReturn               status;
1846
1847            driver = (IOBlockStorageDriver *) minor->media->getProvider();
1848            driver = OSDynamicCast(IOBlockStorageDriver, driver);
1849
1850            // Determine whether this media has an IOBlockStorageDriver parent.
1851
1852            if ( driver == 0 )  { error = ENOTTY;  break; }
1853
1854            // Request that the drive enter an idle state.
1855
1856            status = driver->requestIdle();
1857            error  = minor->media->errnoFromReturn(status);
1858
1859        } break;
1860
1861        case DKIOCGETBSDUNIT:                                    // (uint32_t *)
1862        {
1863            //
1864            // This ioctl returns the BSD unit of the media object.
1865            //
1866
1867            OSNumber * number = OSDynamicCast(
1868                         /* class  */ OSNumber,
1869                         /* object */ minor->media->getProperty(
1870                                 /* key   */ kIOBSDUnitKey ) );
1871            if ( number )
1872                *(uint32_t *)data = number->unsigned32BitValue();
1873            else
1874                *(uint32_t *)data = 0;
1875
1876        } break;
1877
1878        case DKIOCGETFIRMWAREPATH:                     // (dk_firmware_path_t *)
1879        {
1880            //
1881            // This ioctl returns the open firmware path for this media object.
1882            //
1883
1884            int    l = sizeof(((dk_firmware_path_t *)data)->path);
1885            char * p = ((dk_firmware_path_t *)data)->path;
1886
1887            if ( minor->media->getPath(p, &l, gIODTPlane) && strchr(p, ':') )
1888                strlcpy(p, strchr(p, ':') + 1, l);     // (strip the plane name)
1889            else
1890                error = EINVAL;
1891
1892        } break;
1893
1894        case DKIOCISSOLIDSTATE:                                  // (uint32_t *)
1895        {
1896            //
1897            // This ioctl returns truth if the device is solid state.
1898            //
1899
1900            OSDictionary * dictionary = OSDynamicCast(
1901                         /* class  */ OSDictionary,
1902                         /* object */ minor->media->getProperty(
1903                                 /* key   */ kIOPropertyDeviceCharacteristicsKey,
1904                                 /* plane */ gIOServicePlane ) );
1905
1906            *(uint32_t *)data = false;
1907
1908            if ( dictionary )
1909            {
1910                OSString * string = OSDynamicCast(
1911                         /* class  */ OSString,
1912                         /* object */ dictionary->getObject(
1913                                 /* key   */ kIOPropertyMediumTypeKey ) );
1914
1915                if ( string && string->isEqualTo(kIOPropertyMediumTypeSolidStateKey) )
1916                    *(uint32_t *)data = true;
1917            }
1918
1919        } break;
1920
1921        case DKIOCISVIRTUAL:                                     // (uint32_t *)
1922        {
1923            //
1924            // This ioctl returns truth if the device is virtual.
1925            //
1926
1927            OSDictionary * dictionary = OSDynamicCast(
1928                         /* class  */ OSDictionary,
1929                         /* object */ minor->media->getProperty(
1930                                 /* key   */ kIOPropertyProtocolCharacteristicsKey,
1931                                 /* plane */ gIOServicePlane ) );
1932
1933            *(uint32_t *)data = false;
1934
1935            if ( dictionary )
1936            {
1937                OSString * string = OSDynamicCast(
1938                         /* class  */ OSString,
1939                         /* object */ dictionary->getObject(
1940                                 /* key   */ kIOPropertyPhysicalInterconnectTypeKey ) );
1941
1942                if ( string && string->isEqualTo(kIOPropertyPhysicalInterconnectTypeVirtual) )
1943                    *(uint32_t *)data = true;
1944            }
1945
1946        } break;
1947
1948        case DKIOCGETBASE:                                       // (uint64_t *)
1949        {
1950            //
1951            // This ioctl returns the base of the media object.
1952            //
1953
1954            *(uint64_t *)data = minor->media->getBase();
1955
1956        } break;
1957
1958        case DKIOCGETFEATURES:                                   // (uint32_t *)
1959        {
1960            //
1961            // This ioctl returns the features of the media object.
1962            //
1963
1964            OSDictionary * dictionary = OSDynamicCast(
1965                         /* class  */ OSDictionary,
1966                         /* object */ minor->media->getProperty(
1967                                 /* key   */ kIOStorageFeaturesKey,
1968                                 /* plane */ gIOServicePlane ) );
1969
1970            *(uint32_t *)data = 0;
1971
1972            if ( dictionary )
1973            {
1974                OSBoolean * boolean;
1975
1976                boolean = OSDynamicCast(
1977                         /* class  */ OSBoolean,
1978                         /* object */ dictionary->getObject(
1979                                 /* key   */ kIOStorageFeatureUnmap ) );
1980
1981                if ( boolean == kOSBooleanTrue )
1982                    *(uint32_t *)data |= DK_FEATURE_UNMAP;
1983
1984                boolean = OSDynamicCast(
1985                         /* class  */ OSBoolean,
1986                         /* object */ dictionary->getObject(
1987                                 /* key   */ kIOStorageFeatureForceUnitAccess ) );
1988
1989                if ( boolean == kOSBooleanTrue )
1990                    *(uint32_t *)data |= DK_FEATURE_FORCE_UNIT_ACCESS;
1991            }
1992
1993        } break;
1994
1995        case DKIOCGETTHROTTLEMASK:                               // (uint64_t *)
1996        {
1997            //
1998            // This ioctl returns the throttle mask for the media object.
1999            //
2000
2001            *( ( uint64_t * ) data ) = _IOMediaBSDClientGetThrottleMask( minor->media );
2002
2003        } break;
2004
2005        default:
2006        {
2007            //
2008            // Call the foreign ioctl handler for all other ioctls.
2009            //
2010
2011            error = minor->client->ioctl(dev, cmd, data, flags, proc);
2012
2013        } break;
2014    }
2015
2016    return error;                                       // (return error status)
2017}
2018
2019int dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int flags, proc_t proc)
2020{
2021    //
2022    // dkioctl_bdev performs operations other than a read or write, specific to
2023    // the block device.
2024    //
2025
2026    int         error = 0;
2027    MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
2028
2029    if ( minor->isOrphaned )  return EBADF;               // (is minor in flux?)
2030
2031    //
2032    // Process the ioctl.
2033    //
2034
2035    switch ( cmd )
2036    {
2037        case DKIOCGETBLOCKSIZE:                                  // (uint32_t *)
2038        {
2039            //
2040            // This ioctl returns the preferred (or overrided) block size of the
2041            // media object.
2042            //
2043
2044            *(uint32_t *)data = minor->bdevBlockSize;
2045
2046        } break;
2047
2048        case DKIOCSETBLOCKSIZE:                                  // (uint32_t *)
2049        {
2050            //
2051            // This ioctl overrides the block size for the media object, for the
2052            // duration of all block device opens at this minor.
2053            //
2054
2055            if ( *(uint32_t *)data > 0 )
2056                minor->bdevBlockSize = (UInt64) (*(uint32_t *)data);
2057            else
2058                error = EINVAL;
2059
2060        } break;
2061
2062#ifndef __LP64__
2063        case DKIOCGETBLOCKCOUNT32:                               // (uint32_t *)
2064        {
2065            //
2066            // This ioctl returns the size of the media object in blocks.  The
2067            // implied block size is returned by DKIOCGETBLOCKSIZE.
2068            //
2069
2070            if ( minor->bdevBlockSize )
2071                *(uint32_t *)data = ( minor->media->getSize() /
2072                                      minor->bdevBlockSize    );
2073            else
2074                *(uint32_t *)data = 0;
2075
2076        } break;
2077#endif /* !__LP64__ */
2078
2079        case DKIOCGETBLOCKCOUNT:                                 // (uint64_t *)
2080        {
2081            //
2082            // This ioctl returns the size of the media object in blocks.  The
2083            // implied block size is returned by DKIOCGETBLOCKSIZE.
2084            //
2085
2086            if ( minor->bdevBlockSize )
2087                *(uint64_t *)data = ( minor->media->getSize() /
2088                                      minor->bdevBlockSize    );
2089            else
2090                *(uint64_t *)data = 0;
2091
2092        } break;
2093
2094        case DKIOCLOCKPHYSICALEXTENTS:                                 // (void)
2095        {
2096            bool success;
2097
2098            success = minor->media->lockPhysicalExtents( minor->client );
2099
2100            if ( success == false )
2101            {
2102                error = ENOTSUP;
2103            }
2104
2105        } break;
2106
2107        case DKIOCGETPHYSICALEXTENT:                   // (dk_physical_extent_t)
2108        {
2109            dk_physical_extent_t * request;
2110
2111            request = ( dk_physical_extent_t * ) data;
2112
2113            if ( DKIOC_IS_RESERVED( data, 0xFFFF0000 ) == false )
2114            {
2115                IOStorage * media;
2116
2117                media = minor->media->copyPhysicalExtent( minor->client, &request->offset, &request->length );
2118
2119                if ( media )
2120                {
2121                    OSNumber * majorID;
2122
2123                    majorID = OSDynamicCast( OSNumber, media->getProperty( kIOBSDMajorKey ) );
2124
2125                    if ( majorID )
2126                    {
2127                        OSNumber * minorID;
2128
2129                        minorID = OSDynamicCast( OSNumber, media->getProperty( kIOBSDMinorKey ) );
2130
2131                        if ( minorID )
2132                        {
2133                            request->dev = makedev( majorID->unsigned32BitValue( ), minorID->unsigned32BitValue( ) );
2134                        }
2135                        else
2136                        {
2137                            error = ENODEV;
2138                        }
2139                    }
2140                    else
2141                    {
2142                        error = ENODEV;
2143                    }
2144
2145                    media->release( );
2146                }
2147                else
2148                {
2149                    error = ENOTSUP;
2150                }
2151            }
2152            else
2153            {
2154                error = EINVAL;
2155            }
2156
2157        } break;
2158
2159        case DKIOCUNLOCKPHYSICALEXTENTS:                               // (void)
2160        {
2161            minor->media->unlockPhysicalExtents( minor->client );
2162
2163        } break;
2164
2165        case DKIOCGETMAXPRIORITYCOUNT:                           // (uint32_t *)
2166        {
2167            //
2168            // This ioctl returns the maximum priority depth of the device.
2169            //
2170
2171            OSNumber * number = OSDynamicCast(
2172                         /* class  */ OSNumber,
2173                         /* object */ minor->media->getProperty(
2174                                 /* key   */ kIOMaximumPriorityCountKey,
2175                                 /* plane */ gIOServicePlane ) );
2176            if ( number )
2177                *(uint32_t *)data = number->unsigned32BitValue();
2178            else
2179                *(uint32_t *)data = 0;
2180
2181        } break;
2182
2183        default:
2184        {
2185            //
2186            // Call the common ioctl handler for all other ioctls.
2187            //
2188
2189            error = dkioctl(dev, cmd, data, flags, proc);
2190
2191        } break;
2192    }
2193
2194    return error;                                       // (return error status)
2195}
2196
2197int dkioctl_cdev(dev_t dev, u_long cmd, caddr_t data, int flags, proc_t proc)
2198{
2199    //
2200    // dkioctl_cdev performs operations other than a read or write, specific to
2201    // the character device.
2202    //
2203
2204    int         error = 0;
2205    MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
2206
2207    if ( minor->isOrphaned )  return EBADF;               // (is minor in flux?)
2208
2209    //
2210    // Process the ioctl.
2211    //
2212
2213    switch ( cmd )
2214    {
2215#if TARGET_OS_EMBEDDED
2216        case _DKIOCSETSTATIC:                                          // (void)
2217        {
2218            minor->cdevOptions |= kIOStorageOptionIsStatic;
2219
2220        } break;
2221#endif /* TARGET_OS_EMBEDDED */
2222
2223        default:
2224        {
2225            //
2226            // Call the common ioctl handler for all other ioctls.
2227            //
2228
2229            error = dkioctl(dev, cmd, data, flags, proc);
2230
2231        } break;
2232    }
2233
2234    return error;                                       // (return error status)
2235}
2236
2237int dksize(dev_t dev)
2238{
2239    //
2240    // dksize returns the block size of the media.
2241    //
2242    // This is a departure from BSD 4.4's definition of this function, that is,
2243    // it will not return the size of the disk partition, as would be expected
2244    // in a BSD 4.4 implementation.
2245    //
2246
2247    MinorSlot * minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
2248
2249    if ( minor->isOrphaned )  return 0;                   // (is minor in flux?)
2250
2251    return (int) minor->bdevBlockSize;                    // (return block size)
2252}
2253
2254// =============================================================================
2255// Support For BSD Functions
2256
2257extern "C" task_t get_aiotask();
2258
2259inline task_t get_kernel_task()
2260{
2261    return kernel_task;
2262}
2263
2264inline task_t get_user_task()
2265{
2266    task_t task;
2267
2268    task = get_aiotask();
2269
2270    if ( task == 0 )  task = current_task();
2271
2272    return task;
2273}
2274
2275inline dev_t DKR_GET_DEV(dkr_t dkr, dkrtype_t dkrtype)
2276{
2277    return (dkrtype == DKRTYPE_BUF)
2278           ? buf_device((buf_t)dkr)
2279           : ((dio_t)dkr)->dev;
2280}
2281
2282inline UInt64 DKR_GET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype)
2283{
2284    return (dkrtype == DKRTYPE_BUF)
2285           ? buf_count((buf_t)dkr)
2286           : uio_resid(((dio_t)dkr)->uio);
2287}
2288
2289inline UInt64 DKR_GET_BYTE_START(dkr_t dkr, dkrtype_t dkrtype)
2290{
2291    if (dkrtype == DKRTYPE_BUF)
2292    {
2293        buf_t       bp = (buf_t)dkr;
2294        MinorSlot * minor;
2295
2296        minor = gIOMediaBSDClientGlobals.getMinor(getminor(buf_device(bp)));
2297
2298        return (UInt64)buf_blkno(bp) * minor->bdevBlockSize;
2299    }
2300
2301    return uio_offset(((dio_t)dkr)->uio);
2302}
2303
2304inline bool DKR_IS_READ(dkr_t dkr, dkrtype_t dkrtype)
2305{
2306    return (dkrtype == DKRTYPE_BUF)
2307           ? ((buf_flags((buf_t)dkr) & B_READ) == B_READ)
2308           : ((uio_rw(((dio_t)dkr)->uio)) == UIO_READ);
2309}
2310
2311inline bool DKR_IS_ASYNCHRONOUS(dkr_t dkr, dkrtype_t dkrtype)
2312{
2313    return (dkrtype == DKRTYPE_BUF)
2314           ? true
2315           : false;
2316}
2317
2318inline bool DKR_IS_RAW(dkr_t dkr, dkrtype_t dkrtype)
2319{
2320    return (dkrtype == DKRTYPE_BUF)
2321           ? false
2322           : true;
2323}
2324
2325inline void DKR_SET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype, UInt64 bcount)
2326{
2327    if (dkrtype == DKRTYPE_BUF)
2328        buf_setresid((buf_t)dkr, buf_count((buf_t)dkr) - bcount);
2329    else
2330        uio_setresid(((dio_t)dkr)->uio, uio_resid(((dio_t)dkr)->uio) - bcount);
2331}
2332
2333inline void DKR_RUN_COMPLETION(dkr_t dkr, dkrtype_t dkrtype, IOReturn status)
2334{
2335    if (dkrtype == DKRTYPE_BUF)
2336    {
2337        buf_t       bp = (buf_t)dkr;
2338        MinorSlot * minor;
2339
2340        minor = gIOMediaBSDClientGlobals.getMinor(getminor(buf_device(bp)));
2341
2342        buf_seterror(bp, minor->media->errnoFromReturn(status));     // (error?)
2343        buf_biodone(bp);                                   // (complete request)
2344    }
2345}
2346
2347inline IOMemoryDescriptor * DKR_GET_BUFFER(dkr_t dkr, dkrtype_t dkrtype)
2348{
2349    if (dkrtype == DKRTYPE_BUF)
2350    {
2351        buf_t bp = (buf_t)dkr;
2352        int   flags;
2353
2354        flags = buf_flags(bp);
2355
2356        if ( (flags & B_CLUSTER) )
2357        {
2358            IOOptionBits options = kIOMemoryTypeUPL | kIOMemoryAsReference;
2359
2360            options |= (flags & B_READ) ? kIODirectionIn : kIODirectionOut;
2361
2362            return IOMemoryDescriptor::withOptions(          // (multiple-range)
2363                buf_upl(bp),
2364                buf_count(bp),
2365                buf_uploffset(bp),
2366                0,
2367                options );
2368        }
2369        else
2370        {
2371            return IOMemoryDescriptor::withAddressRange(       // (single-range)
2372                buf_dataptr(bp),
2373                buf_count(bp),
2374                (flags & B_READ) ? kIODirectionIn : kIODirectionOut,
2375                (flags & B_PHYS) ? get_user_task() : get_kernel_task() );
2376        }
2377    }
2378    else
2379    {
2380        IOOptionBits options = kIOMemoryTypeUIO | kIOMemoryAsReference;
2381        uio_t        uio     = ((dio_t)dkr)->uio;
2382
2383        options |= (uio_rw(uio) == UIO_READ) ? kIODirectionIn : kIODirectionOut;
2384
2385        return IOMemoryDescriptor::withOptions(              // (multiple-range)
2386            uio,
2387            uio_iovcnt(uio),
2388            0,
2389            (uio_isuserspace(uio)) ? get_user_task() : get_kernel_task(),
2390            options );
2391    }
2392}
2393
2394inline void * DKR_GET_DRIVER_DATA(dkr_t dkr, dkrtype_t dkrtype)
2395{
2396    return (dkrtype == DKRTYPE_BUF)
2397           ? buf_drvdata((buf_t)dkr)
2398           : ((dio_t)dkr)->drvdata;
2399}
2400
2401inline void DKR_SET_DRIVER_DATA(dkr_t dkr, dkrtype_t dkrtype, void * drvdata)
2402{
2403    if (dkrtype == DKRTYPE_BUF)
2404        buf_setdrvdata((buf_t)dkr, drvdata);
2405    else
2406        ((dio_t)dkr)->drvdata = drvdata;
2407}
2408
2409inline IOStorageAttributes DKR_GET_ATTRIBUTES(dkr_t dkr, dkrtype_t dkrtype)
2410{
2411    IOStorageAttributes attributes = { 0 };
2412
2413    if (dkrtype == DKRTYPE_BUF)
2414    {
2415        buf_t bp = (buf_t)dkr;
2416        int   flags;
2417
2418        flags = buf_flags(bp);
2419
2420        attributes.bufattr = buf_attr(bp);
2421
2422        attributes.options |= (flags & B_FUA          ) ? kIOStorageOptionForceUnitAccess : 0;
2423        attributes.options |= (flags & B_ENCRYPTED_IO ) ? kIOStorageOptionIsEncrypted     : 0;
2424        attributes.options |= (flags & B_STATICCONTENT) ? kIOStorageOptionIsStatic        : 0;
2425    }
2426#if TARGET_OS_EMBEDDED
2427    else
2428    {
2429        dev_t       dev = ((dio_t)dkr)->dev;
2430        MinorSlot * minor;
2431
2432        minor = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
2433
2434        attributes.options |= minor->cdevOptions;
2435    }
2436#endif /* TARGET_OS_EMBEDDED */
2437
2438    return attributes;
2439}
2440///w:start
2441#if !TARGET_OS_EMBEDDED
2442inline bool DKR_DELAY_IDLE_SLEEP(dkr_t dkr, dkrtype_t dkrtype)
2443{
2444    return (dkrtype == DKRTYPE_BUF)
2445           ? bufattr_delayidlesleep(buf_attr((buf_t)dkr))
2446           : false;
2447}
2448#endif /* !TARGET_OS_EMBEDDED */
2449///w:stop
2450
2451int dkreadwrite(dkr_t dkr, dkrtype_t dkrtype)
2452{
2453    //
2454    // dkreadwrite performs a read or write operation.
2455    //
2456
2457    IOStorageAttributes  attributes;
2458    IOMemoryDescriptor * buffer;
2459    register UInt64      byteCount;
2460    register UInt64      byteStart;
2461    UInt64               mediaSize;
2462    MinorSlot *          minor;
2463    IOReturn             status;
2464
2465    DKR_SET_DRIVER_DATA(dkr, dkrtype, 0);
2466
2467    minor = gIOMediaBSDClientGlobals.getMinor(getminor(DKR_GET_DEV(dkr, dkrtype)));
2468
2469    if ( minor->isOrphaned )                              // (is minor in flux?)
2470    {
2471        status = kIOReturnNoMedia;
2472        goto dkreadwriteErr;
2473    }
2474
2475    if ( minor->media->isFormatted() == false )       // (is media unformatted?)
2476    {
2477        status = kIOReturnUnformattedMedia;
2478        goto dkreadwriteErr;
2479    }
2480
2481    byteCount = DKR_GET_BYTE_COUNT(dkr, dkrtype);            // (get byte count)
2482    byteStart = DKR_GET_BYTE_START(dkr, dkrtype);            // (get byte start)
2483    mediaSize = minor->media->getSize();                     // (get media size)
2484
2485    //
2486    // Reads that start at (or perhaps past) the end-of-media are not considered
2487    // errors, even though no data is transferred, while writes at (or past) the
2488    // end-of-media do indeed return errors under BSD semantics.
2489    //
2490
2491    if ( byteStart >= mediaSize )     // (is start at or past the end-of-media?)
2492    {
2493        status = DKR_IS_READ(dkr,dkrtype) ? kIOReturnSuccess : kIOReturnIOError;
2494        goto dkreadwriteErr;
2495    }
2496
2497    //
2498    // Reads and writes, via the character device, that do not start or end on a
2499    // media block boundary are considered errors under BSD semantics.
2500    //
2501
2502    if ( DKR_IS_RAW(dkr, dkrtype) )
2503    {
2504        UInt64 mediaBlockSize = minor->media->getPreferredBlockSize();
2505
2506        if ( (byteStart % mediaBlockSize) || (byteCount % mediaBlockSize) )
2507        {
2508            status = kIOReturnNotAligned;
2509            goto dkreadwriteErr;
2510        }
2511    }
2512
2513    //
2514    // Build a descriptor which describes the buffer involved in the transfer.
2515    //
2516
2517    buffer = DKR_GET_BUFFER(dkr, dkrtype);
2518
2519    if ( buffer == 0 )                                           // (no buffer?)
2520    {
2521        status = kIOReturnNoMemory;
2522        goto dkreadwriteErr;
2523    }
2524
2525    //
2526    // Reads and writes that extend beyond the end-of-media are not considered
2527    // errors under BSD semantics.  We are to transfer as many bytes as can be
2528    // read or written from the medium and return no error.  This differs from
2529    // IOMedia semantics which is to fail the entire request without copying a
2530    // single byte should it include something past the end-of-media.  We must
2531    // adapt the IOMedia semantics to look like BSD semantics here.
2532    //
2533    // Clip the transfer buffer should this be a short read or write request.
2534    //
2535
2536    if ( byteCount > mediaSize - byteStart )           // (clip at end-of-media)
2537    {
2538        IOMemoryDescriptor * originalBuffer = buffer;
2539
2540        buffer = IOSubMemoryDescriptor::withSubRange(
2541                           /* descriptor    */ originalBuffer,
2542                           /* withOffset    */ 0,
2543                           /* withLength    */ mediaSize - byteStart,
2544                           /* withDirection */ originalBuffer->getDirection() );
2545
2546        originalBuffer->release();   // (either retained above or about to fail)
2547
2548        if ( buffer == 0 )                                      // (no buffer?)
2549        {
2550            status = kIOReturnNoMemory;
2551            goto dkreadwriteErr;
2552        }
2553    }
2554
2555    //
2556    // Prepare the transfer.
2557    //
2558
2559    if ( buffer->prepare() != kIOReturnSuccess )         // (prepare the buffer)
2560    {
2561        buffer->release();
2562        status = kIOReturnVMError;            // (wiring or permissions failure)
2563        goto dkreadwriteErr;
2564    }
2565
2566    //
2567    // Execute the transfer.
2568    //
2569
2570    attributes = DKR_GET_ATTRIBUTES(dkr, dkrtype);
2571
2572    DKR_SET_DRIVER_DATA(dkr, dkrtype, buffer);
2573
2574///w:start
2575#if !TARGET_OS_EMBEDDED
2576    if ( DKR_DELAY_IDLE_SLEEP(dkr, dkrtype) )
2577    {
2578        IOPMDriverAssertionID assertionID;
2579        AbsoluteTime          assertionTime;
2580
2581        gIOMediaBSDClientGlobals.lockAssertion();
2582
2583        clock_interval_to_deadline(60, NSEC_PER_SEC, &assertionTime);
2584
2585        gIOMediaBSDClientGlobals.setAssertionTime(assertionTime);
2586
2587        assertionID = gIOMediaBSDClientGlobals.getAssertionID();
2588
2589        if ( assertionID == kIOPMUndefinedDriverAssertionID )
2590        {
2591            assertionID = IOService::getPMRootDomain()->createPMAssertion(
2592                    /* type        */ kIOPMDriverAssertionReservedBit7,
2593                    /* level       */ kIOPMDriverAssertionLevelOn,
2594                    /* service     */ minor->client,
2595                    /* description */ "com.apple.iokit.IOStorageFamily" );
2596
2597            if ( assertionID != kIOPMUndefinedDriverAssertionID )
2598            {
2599                gIOMediaBSDClientGlobals.setAssertionID(assertionID);
2600
2601                thread_call_enter_delayed(
2602                        /* call        */ gIOMediaBSDClientGlobals.getAssertionCall(),
2603                        /* deadline    */ assertionTime );
2604            }
2605        }
2606
2607        gIOMediaBSDClientGlobals.unlockAssertion();
2608    }
2609#endif /* !TARGET_OS_EMBEDDED */
2610///w:stop
2611    if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) )       // (an asynchronous request?)
2612    {
2613        IOStorageCompletion completion;
2614
2615        completion.target    = dkr;
2616        completion.action    = dkreadwritecompletion;
2617        completion.parameter = (void *) dkrtype;
2618
2619        if ( DKR_IS_READ(dkr, dkrtype) )                            // (a read?)
2620        {
2621            minor->media->read(  /* client     */ minor->client,
2622                                 /* byteStart  */ byteStart,
2623                                 /* buffer     */ buffer,
2624                                 /* attributes */ &attributes,
2625                                 /* completion */ &completion );         // (go)
2626        }
2627        else                                                       // (a write?)
2628        {
2629            minor->media->write( /* client     */ minor->client,
2630                                 /* byteStart  */ byteStart,
2631                                 /* buffer     */ buffer,
2632                                 /* attributes */ &attributes,
2633                                 /* completion */ &completion );         // (go)
2634        }
2635
2636        status = kIOReturnSuccess;
2637    }
2638    else                                             // (a synchronous request?)
2639    {
2640        if ( DKR_IS_READ(dkr, dkrtype) )                            // (a read?)
2641        {
2642            status = minor->media->read(
2643                                 /* client          */ minor->client,
2644                                 /* byteStart       */ byteStart,
2645                                 /* buffer          */ buffer,
2646#ifdef __LP64__
2647                                 /* attributes      */ &attributes,
2648#endif /* __LP64__ */
2649                                 /* actualByteCount */ &byteCount );     // (go)
2650        }
2651        else                                                       // (a write?)
2652        {
2653            status = minor->media->write(
2654                                 /* client          */ minor->client,
2655                                 /* byteStart       */ byteStart,
2656                                 /* buffer          */ buffer,
2657#ifdef __LP64__
2658                                 /* attributes      */ &attributes,
2659#endif /* __LP64__ */
2660                                 /* actualByteCount */ &byteCount );     // (go)
2661        }
2662
2663        dkreadwritecompletion(dkr, (void *)dkrtype, status, byteCount);
2664    }
2665
2666    return minor->media->errnoFromReturn(status);       // (return error status)
2667
2668dkreadwriteErr:
2669
2670    dkreadwritecompletion(dkr, (void *)dkrtype, status, 0);
2671
2672    return minor->media->errnoFromReturn(status);       // (return error status)
2673}
2674
2675void dkreadwritecompletion( void *   target,
2676                            void *   parameter,
2677                            IOReturn status,
2678                            UInt64   actualByteCount )
2679{
2680    //
2681    // dkreadwritecompletion cleans up after a read or write operation.
2682    //
2683
2684    dkr_t       dkr     = (dkr_t) target;
2685    dkrtype_t   dkrtype = (dkrtype_t) (uintptr_t) parameter;
2686    dev_t       dev     = DKR_GET_DEV(dkr, dkrtype);
2687    void *      drvdata = DKR_GET_DRIVER_DATA(dkr, dkrtype);
2688    MinorSlot * minor   = gIOMediaBSDClientGlobals.getMinor(getminor(dev));
2689
2690    if ( drvdata )                                            // (has a buffer?)
2691    {
2692        IOMemoryDescriptor * buffer = (IOMemoryDescriptor *) drvdata;
2693
2694        buffer->complete();                             // (complete the buffer)
2695        buffer->release();                 // (release our retain on the buffer)
2696    }
2697
2698    if ( status != kIOReturnSuccess )                         // (has an error?)
2699    {
2700        if ( status != kIOReturnNotPermitted )
2701        {
2702            IOLog("%s: %s.\n", minor->name, minor->media->stringFromReturn(status));
2703        }
2704    }
2705
2706    if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) )       // (an asynchronous request?)
2707    {
2708        DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount);   // (set byte count)
2709        DKR_RUN_COMPLETION(dkr, dkrtype, status);            // (run completion)
2710    }
2711    else
2712    {
2713        DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount);   // (set byte count)
2714    }
2715}
2716///w:start
2717#if !TARGET_OS_EMBEDDED
2718void dkreadwriteassertion(thread_call_param_t param0, thread_call_param_t param1)
2719{
2720    AbsoluteTime assertionTime;
2721
2722    gIOMediaBSDClientGlobals.lockAssertion();
2723
2724    assertionTime = gIOMediaBSDClientGlobals.getAssertionTime();
2725
2726    if ( __OSAbsoluteTime(assertionTime) < mach_absolute_time() )
2727    {
2728        IOPMDriverAssertionID assertionID;
2729
2730        assertionID = gIOMediaBSDClientGlobals.getAssertionID();
2731
2732        IOService::getPMRootDomain()->releasePMAssertion(assertionID);
2733
2734        gIOMediaBSDClientGlobals.setAssertionID(kIOPMUndefinedDriverAssertionID);
2735    }
2736    else
2737    {
2738        thread_call_enter_delayed(
2739                /* call        */ gIOMediaBSDClientGlobals.getAssertionCall(),
2740                /* deadline    */ assertionTime );
2741    }
2742
2743    gIOMediaBSDClientGlobals.unlockAssertion();
2744}
2745#endif /* !TARGET_OS_EMBEDDED */
2746///w:stop
2747
2748// =============================================================================
2749// AnchorTable Class
2750
2751AnchorTable::AnchorTable()
2752{
2753    //
2754    // Initialize this object's minimal state.
2755    //
2756
2757    _table      = 0;
2758    _tableCount = 0;
2759}
2760
2761AnchorTable::~AnchorTable()
2762{
2763    //
2764    // Free all of this object's outstanding resources.
2765    //
2766
2767    for ( UInt32 anchorID = 0; anchorID < _tableCount; anchorID++ )
2768        if ( _table[anchorID].isAssigned )  remove(anchorID);
2769
2770    if ( _table )  IODelete(_table, AnchorSlot, _tableCount);
2771}
2772
2773UInt32 AnchorTable::insert(IOService * anchor, void * key)
2774{
2775    //
2776    // This method inserts the specified anchor into an unassigned slot in the
2777    // anchor table and returns its ID (or kInvalidAnchorID on a failure).
2778    //
2779    // Note that the anchor is transparently removed from the table should the
2780    // anchor terminate (or it is at least marked obsolete,  should references
2781    // to the anchor still exist in the minor table).
2782    //
2783
2784    UInt32       anchorID;
2785    IONotifier * notifier;
2786
2787    // Search for an unassigned slot in the anchor table.
2788
2789    for ( anchorID = 0; anchorID < _tableCount; anchorID++ )
2790        if ( _table[anchorID].isAssigned == false )  break;
2791
2792    // Was an unassigned slot found?  If not, grow the table.
2793
2794    if ( anchorID == _tableCount )
2795    {
2796        AnchorSlot * newTable;
2797        UInt32       newTableCount;
2798
2799        // We must expand the anchor table since no more slots are available.
2800
2801        if ( _tableCount >= kAnchorsMaxCount )  return kInvalidAnchorID;
2802
2803        newTableCount = min(kAnchorsAddCount + _tableCount, kAnchorsMaxCount);
2804        newTable      = IONew(AnchorSlot, newTableCount);
2805
2806        if ( newTable == 0 )  return kInvalidAnchorID;
2807
2808        bzero(newTable, newTableCount * sizeof(AnchorSlot));
2809
2810        // Copy over the old table's entries, then free the old table.
2811
2812        if ( _table )
2813        {
2814            bcopy(_table, newTable, _tableCount * sizeof(AnchorSlot));
2815            IODelete(_table, AnchorSlot, _tableCount);
2816        }
2817
2818        // Obtain the next unassigned index (simple since we know the size of
2819        // the old table),  then update our instance variables to reflect the
2820        // new tables.
2821
2822        anchorID    = _tableCount;
2823        _table      = newTable;
2824        _tableCount = newTableCount;
2825    }
2826
2827    // Create a notification handler for the anchor's termination (post-stop);
2828    // the handler will remove the anchor transparently from the table if the
2829    // anchor terminates (or at least marks it obsolete, if references to the
2830    // anchor still exist in the minor table).
2831
2832    notifier = anchor->registerInterest(
2833                          /* type        */ gIOGeneralInterest,
2834                          /* action      */ anchorWasNotified,
2835                          /* target      */ this,
2836                          /* parameter   */ 0 );
2837
2838    if ( notifier == 0 )  return kInvalidAnchorID;
2839
2840    // Zero the new slot, fill it in, and retain the anchor object.
2841
2842    bzero(&_table[anchorID], sizeof(AnchorSlot)); // (zero slot)
2843
2844    _table[anchorID].isAssigned = true;           // (fill in slot)
2845    _table[anchorID].isObsolete = false;
2846    _table[anchorID].anchor     = anchor;
2847    _table[anchorID].key        = key;
2848    _table[anchorID].notifier   = notifier;
2849
2850    _table[anchorID].anchor->retain();            // (retain anchor)
2851
2852    return anchorID;
2853}
2854
2855void AnchorTable::remove(UInt32 anchorID)
2856{
2857    //
2858    // This method removes the specified anchor from the anchor table.
2859    //
2860
2861    assert(anchorID < _tableCount);
2862    assert(_table[anchorID].isAssigned);
2863
2864    // Release the resources retained in the anchor slot and zero it.
2865
2866    _table[anchorID].notifier->remove();
2867    _table[anchorID].anchor->release();           // (release anchor)
2868
2869    bzero(&_table[anchorID], sizeof(AnchorSlot)); // (zero slot)
2870}
2871
2872void AnchorTable::obsolete(UInt32 anchorID)
2873{
2874    //
2875    // This method obsoletes the specified anchor, that is, the slot is marked
2876    // as obsolete and will be removed later via the minor table remove method
2877    // once it detects references to the anchor ID drop to 0.   Once obsoleted,
2878    // the anchor can be considered to be removed, since it will not appear in
2879    // locate searches, even though behind the scenes it still occupies a slot.
2880    //
2881
2882    assert(anchorID < _tableCount);
2883    assert(_table[anchorID].isAssigned);
2884
2885    // Mark the anchor as obsolete so that it can be removed from the table as
2886    // soon as all its references go away (minor table's responsibility).
2887
2888    _table[anchorID].isObsolete = true;
2889}
2890
2891UInt32 AnchorTable::locate(IOService * anchor)
2892{
2893    //
2894    // This method searches for the specified anchor in the anchor table and
2895    // returns its ID (or kInvalidAnchorID on a failure).  It would find the
2896    // first occurrence of the anchor in case multiple entries with the same
2897    // anchor object exist.  It ignores slots marked as obsolete.
2898    //
2899
2900    for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
2901    {
2902        if ( _table[anchorID].isAssigned != false  &&
2903             _table[anchorID].isObsolete == false  &&
2904             _table[anchorID].anchor     == anchor )  return anchorID;
2905    }
2906
2907    return kInvalidAnchorID;
2908}
2909
2910UInt32 AnchorTable::locate(IOService * anchor, void * key)
2911{
2912    //
2913    // This method searches for the specified anchor and key pair in the anchor
2914    // table and returns its ID (or kInvalidAnchorID on a failure).  It ignores
2915    // slots marked as obsolete.
2916    //
2917
2918    for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
2919    {
2920        if ( _table[anchorID].isAssigned != false  &&
2921             _table[anchorID].isObsolete == false  &&
2922             _table[anchorID].anchor     == anchor &&
2923             _table[anchorID].key        == key    )  return anchorID;
2924    }
2925
2926    return kInvalidAnchorID;
2927}
2928
2929UInt32 AnchorTable::update(IOService * anchor, void * key)
2930{
2931    //
2932    // This method searches for the specified anchor in the anchor table and
2933    // updates its key value if no references to it exist in the minor table
2934    // or if the references in the minor table are all obsolete.  It returns
2935    // the updated anchor ID (or kInvalidAnchorID on a failure).  It ignores
2936    // slots marked as obsolete.
2937    //
2938
2939    MinorTable * minors = gIOMediaBSDClientGlobals.getMinors();
2940
2941    for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
2942    {
2943        if ( _table[anchorID].isAssigned != false  &&
2944             _table[anchorID].isObsolete == false  &&
2945             _table[anchorID].anchor     == anchor )
2946        {
2947            if ( minors->hasReferencesToAnchorID(anchorID, true) == false )
2948            {
2949                _table[anchorID].key = key;
2950                return anchorID;
2951            }
2952        }
2953    }
2954
2955    return kInvalidAnchorID;
2956}
2957
2958bool AnchorTable::isObsolete(UInt32 anchorID)
2959{
2960    //
2961    // Determine whether the specified anchor ID is marked as obsolete.
2962    //
2963
2964    assert(anchorID < _tableCount);
2965    assert(_table[anchorID].isAssigned);
2966
2967    return _table[anchorID].isObsolete ? true : false;
2968}
2969
2970IOReturn AnchorTable::anchorWasNotified( void *      /* target */,
2971                                         void *      /* parameter */,
2972                                         UInt32      messageType,
2973                                         IOService * anchor,
2974                                         void *      /* messageArgument */,
2975                                         vm_size_t   /* messageArgumentSize */ )
2976{
2977    //
2978    // Notification handler for anchors.
2979    //
2980
2981    AnchorTable * anchors = gIOMediaBSDClientGlobals.getAnchors();
2982    UInt32        anchorID;
2983    MinorTable *  minors  = gIOMediaBSDClientGlobals.getMinors();
2984
2985    // Determine whether this is a termination notification (post-stop).
2986
2987    if ( messageType != kIOMessageServiceIsTerminated )
2988        return kIOReturnSuccess;
2989
2990    // Disable access to tables.
2991
2992    gIOMediaBSDClientGlobals.lockState();
2993
2994    // Determine whether this anchor is in the anchor table (obsolete occurences
2995    // are skipped in the search, as appropriate, since those anchor IDs will be
2996    // removed as it is).
2997
2998    while ( (anchorID = anchors->locate(anchor)) != kInvalidAnchorID )
2999    {
3000        // Determine whether this anchor still has references from the minor
3001        // table.  If it does, we mark the the anchor as obsolete so that it
3002        // will be removed later, once references to it go to zero (which is
3003        // handled by MinorTable::remove).
3004
3005        if ( minors->hasReferencesToAnchorID(anchorID, false) )
3006            anchors->obsolete(anchorID);
3007        else
3008            anchors->remove(anchorID);
3009    }
3010
3011    // Enable access to tables.
3012
3013    gIOMediaBSDClientGlobals.unlockState();
3014
3015    return kIOReturnSuccess;
3016}
3017
3018// =============================================================================
3019// MinorTable Class
3020
3021MinorTable::MinorTable()
3022{
3023    //
3024    // Initialize this object's minimal state.
3025    //
3026
3027    _table.buckets = IONew(MinorSlot *, kMinorsBucketCount);
3028    _tableCount    = 0;
3029
3030    if ( _table.buckets )
3031        bzero(_table.buckets, kMinorsBucketCount * sizeof(MinorSlot *));
3032}
3033
3034MinorTable::~MinorTable()
3035{
3036    //
3037    // Free all of this object's outstanding resources.
3038    //
3039
3040    for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
3041        if ( _table[minorID].isAssigned )  remove(minorID);
3042
3043    if ( _table.buckets )
3044    {
3045        for ( UInt32 bucketID = 0; _table.buckets[bucketID]; bucketID++ )
3046            IODelete(_table.buckets[bucketID], MinorSlot, kMinorsAddCount);
3047
3048        IODelete(_table.buckets, MinorSlot *, kMinorsBucketCount);
3049    }
3050}
3051
3052UInt32 MinorTable::insert( IOMedia *          media,
3053                           UInt32             anchorID,
3054                           IOMediaBSDClient * client,
3055                           char *             slicePath )
3056{
3057    //
3058    // This method inserts the specified media/anchorID pair into an unassigned
3059    // slot in the minor table and returns its ID (or kInvalidMinorID on error).
3060    //
3061    // Note that the bdev and cdev nodes are published as a result of this call,
3062    // with the name "[r]disk<anchorID><slicePath>".  For instance, "disk2s3s1"
3063    // for an anchorID of 2 and slicePath of "s3s1".
3064    //
3065
3066    void *       bdevNode;
3067    void *       cdevNode;
3068    UInt32       majorID = gIOMediaBSDClientGlobals.getMajorID();
3069    UInt32       minorID;
3070    char *       minorName;
3071    UInt32       minorNameSize;
3072
3073    if ( _table.buckets == 0 )  return kInvalidMinorID;
3074
3075    // Search for an unassigned slot in the minor table.
3076
3077    for ( minorID = 0; minorID < _tableCount; minorID++ )
3078        if ( _table[minorID].isAssigned == false )  break;
3079
3080    // Was an unassigned slot found?  If not, grow the table.
3081
3082    if ( minorID == _tableCount )
3083    {
3084        UInt32 bucketID = _tableCount / kMinorsAddCount;
3085
3086        // We must expand the minor table since no more slots are available.
3087
3088        if ( bucketID >= kMinorsBucketCount )  return kInvalidMinorID;
3089
3090        _table.buckets[bucketID] = IONew(MinorSlot, kMinorsAddCount);
3091
3092        if ( _table.buckets[bucketID] == 0 )  return kInvalidMinorID;
3093
3094        bzero(_table.buckets[bucketID], kMinorsAddCount * sizeof(MinorSlot));
3095
3096        _tableCount += kMinorsAddCount;
3097    }
3098
3099    // Create a buffer large enough to hold the full name of the minor.
3100
3101    minorNameSize = strlen("disk#");
3102    for (unsigned temp = anchorID; temp >= 10; temp /= 10)  minorNameSize++;
3103    minorNameSize += strlen(slicePath);
3104    minorNameSize += 1;
3105    minorName = IONew(char, minorNameSize);
3106
3107    // Create a block and character device node in BSD for this media.
3108
3109    bdevNode = devfs_make_node( /* dev        */ makedev(majorID, minorID),
3110                                /* type       */ DEVFS_BLOCK,
3111                                /* owner      */ UID_ROOT,
3112                                /* group      */ GID_OPERATOR,
3113                                /* permission */ 0640,
3114                                /* name (fmt) */ "disk%d%s",
3115                                /* name (arg) */ anchorID,
3116                                /* name (arg) */ slicePath );
3117
3118    cdevNode = devfs_make_node( /* dev        */ makedev(majorID, minorID),
3119                                /* type       */ DEVFS_CHAR,
3120                                /* owner      */ UID_ROOT,
3121                                /* group      */ GID_OPERATOR,
3122                                /* permission */ 0640,
3123                                /* name (fmt) */ "rdisk%d%s",
3124                                /* name (arg) */ anchorID,
3125                                /* name (arg) */ slicePath );
3126
3127    if ( minorName == 0 || bdevNode == 0 || cdevNode == 0 )
3128    {
3129        if ( cdevNode )   devfs_remove(cdevNode);
3130        if ( bdevNode )   devfs_remove(bdevNode);
3131        if ( minorName )  IODelete(minorName, char, minorNameSize);
3132
3133        return kInvalidMinorID;
3134    }
3135
3136    // Construct a name for the node.
3137
3138    snprintf(minorName, minorNameSize, "disk%d%s", (int) anchorID, slicePath);
3139    assert(strlen(minorName) + 1 == minorNameSize);
3140
3141    // Zero the new slot, fill it in, and retain the appropriate objects.
3142
3143    bzero(&_table[minorID], sizeof(MinorSlot));    // (zero slot)
3144
3145    _table[minorID].isAssigned    = true;          // (fill in slot)
3146    _table[minorID].isObsolete    = false;
3147    _table[minorID].isOrphaned    = false;
3148    _table[minorID].anchorID      = anchorID;
3149    _table[minorID].client        = client;
3150    _table[minorID].media         = media;
3151    _table[minorID].name          = minorName;
3152    _table[minorID].bdevBlockSize = media->getPreferredBlockSize();
3153    _table[minorID].bdevNode      = bdevNode;
3154    _table[minorID].bdevOpen      = 0;
3155    _table[minorID].bdevOpenLevel = kIOStorageAccessNone;
3156    _table[minorID].cdevNode      = cdevNode;
3157    _table[minorID].cdevOpen      = 0;
3158    _table[minorID].cdevOpenLevel = kIOStorageAccessNone;
3159#if TARGET_OS_EMBEDDED
3160    _table[minorID].cdevOptions   = 0;
3161#endif /* TARGET_OS_EMBEDDED */
3162
3163    _table[minorID].client->retain();              // (retain client)
3164    _table[minorID].media->retain();               // (retain media)
3165
3166    return minorID;
3167}
3168
3169void MinorTable::remove(UInt32 minorID)
3170{
3171    //
3172    // This method removes the specified minor from the minor table.
3173    //
3174
3175    UInt32 anchorID;
3176
3177    assert(minorID < _tableCount);
3178    assert(_table[minorID].isAssigned);
3179
3180    assert(_table[minorID].isOrphaned == false);
3181    assert(_table[minorID].bdevOpen == 0);
3182    assert(_table[minorID].cdevOpen == 0);
3183
3184    anchorID = _table[minorID].anchorID;
3185
3186    // Release the resources retained in the minor slot and zero it.
3187
3188    devfs_remove(_table[minorID].cdevNode);
3189    devfs_remove(_table[minorID].bdevNode);
3190    IODelete(_table[minorID].name, char, strlen(_table[minorID].name) + 1);
3191    _table[minorID].client->release();             // (release client)
3192    _table[minorID].media->release();              // (release media)
3193
3194    bzero(&_table[minorID], sizeof(MinorSlot));    // (zero slot)
3195
3196    // Determine whether the associated anchor ID is marked as obsolete.  If it
3197    // is and there are no other references to the anchor ID in the minor table,
3198    // we remove the anchor ID from the anchor table.
3199
3200    if ( gIOMediaBSDClientGlobals.getAnchors()->isObsolete(anchorID) )
3201    {
3202        if ( hasReferencesToAnchorID(anchorID, false) == false )
3203            gIOMediaBSDClientGlobals.getAnchors()->remove(anchorID);
3204    }
3205}
3206
3207UInt32 MinorTable::update( IOMedia *          media,
3208                           UInt32             anchorID,
3209                           IOMediaBSDClient * client,
3210                           char *             slicePath )
3211{
3212    //
3213    // This method searches for the specified anchor ID and slice path pair in
3214    // the minor table and updates it.  An update would be an unusual occasion
3215    // as new anchors are assigned when two media trees are found to share the
3216    // same anchor.  It would occur in one specific circumstance: on the minor
3217    // slot through which a DKIOCFORMAT was issued.  The minor slot would have
3218    // been marked in "open flux", the format would have been issued, then the
3219    // media objects terminated, the minor slot marked obsolete, and the media
3220    // objects republished.  The anchor ID would have one reference, the minor
3221    // slot with the DKIOCFORMAT still outstanding.  AnchorTable::update would
3222    // notice the one reference is orphaned and accept the reuse of the anchor
3223    // ID.  MinorTable::update would notice the orphaned minor slot and update
3224    // it with the new media object and media bsd client object, and clear its
3225    // obsolete state and "open flux" state, once the new media object arrives.
3226    //
3227
3228    UInt32 minorID;
3229    char * minorName;
3230    UInt32 minorNameSize;
3231
3232    // Create a buffer large enough to hold the full name of the minor.
3233
3234    minorNameSize = strlen("disk#");
3235    for (unsigned temp = anchorID; temp >= 10; temp /= 10)  minorNameSize++;
3236    minorNameSize += strlen(slicePath);
3237    minorNameSize += 1;
3238    minorName = IONew(char, minorNameSize);
3239
3240    if ( minorName == 0 )  return kInvalidMinorID;
3241
3242    // Construct a name for the node.
3243
3244    snprintf(minorName, minorNameSize, "disk%d%s", (int) anchorID, slicePath);
3245    assert(strlen(minorName) + 1 == minorNameSize);
3246
3247    // Search for an orphaned slot in the minor table with our minor name.
3248
3249    for ( minorID = 0; minorID < _tableCount; minorID++ )
3250    {
3251        if ( _table[minorID].isAssigned              != false    &&
3252             _table[minorID].isObsolete              != false    &&
3253             _table[minorID].isOrphaned              != false    &&
3254             _table[minorID].anchorID                == anchorID &&
3255             strcmp(_table[minorID].name, minorName) == 0        )  break;
3256    }
3257
3258    IODelete(minorName, char, minorNameSize);
3259
3260    if ( minorID == _tableCount )  return kInvalidMinorID;
3261
3262    // Update the slot and retain the appropriate objects.
3263
3264    _table[minorID].client->release();             // (release client)
3265    _table[minorID].media->release();              // (release media)
3266
3267    _table[minorID].isObsolete = false;            // (update slot)
3268    _table[minorID].isOrphaned = false;
3269    _table[minorID].client     = client;
3270    _table[minorID].media      = media;
3271
3272    _table[minorID].client->retain();              // (retain client)
3273    _table[minorID].media->retain();               // (retain media)
3274
3275    return minorID;
3276}
3277
3278UInt32 MinorTable::locate(IOMedia * media)
3279{
3280    //
3281    // This method searches for the specified media in the minor table and
3282    // returns its ID (or kInvalidMinorID on an error).   It ignores slots
3283    // marked as obsolete.
3284    //
3285
3286    for (UInt32 minorID = 0; minorID < _tableCount; minorID++)
3287    {
3288        if ( _table[minorID].isAssigned != false &&
3289             _table[minorID].isObsolete == false &&
3290             _table[minorID].media      == media )  return minorID;
3291    }
3292
3293    return kInvalidMinorID;
3294}
3295
3296UInt32 MinorTable::getOpenCountForAnchorID(UInt32 anchorID)
3297{
3298    //
3299    // This method obtains a count of opens on the minors associated with the
3300    // specified anchor ID.  A block device open is counted separately from a
3301    // character device open.
3302    //
3303
3304    UInt32 opens = 0;
3305
3306    for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
3307    {
3308        if ( _table[minorID].isAssigned != false    &&
3309             _table[minorID].anchorID   == anchorID )
3310        {
3311            opens += _table[minorID].bdevOpen;
3312            opens += _table[minorID].cdevOpen;
3313        }
3314    }
3315
3316    return opens;
3317}
3318
3319bool MinorTable::hasReferencesToAnchorID(UInt32 anchorID, bool excludeOrphans)
3320{
3321    //
3322    // This method determines whether there are assigned minors in the minor
3323    // table that refer to the specified anchor ID.  It ignores slots marked
3324    // as obsolete and orphaned, unless excludeOrphans is false.
3325    //
3326
3327    for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
3328    {
3329        if ( _table[minorID].isAssigned != false    &&
3330             _table[minorID].anchorID   == anchorID )
3331        {
3332            if ( excludeOrphans             == false )  return true;
3333            if ( _table[minorID].isObsolete == false )  return true;
3334            if ( _table[minorID].isOrphaned == false )  return true;
3335        }
3336    }
3337
3338    return false;
3339}
3340
3341MinorSlot * MinorTable::getMinor(UInt32 minorID)
3342{
3343    //
3344    // Obtain the structure describing the specified minor.
3345    //
3346
3347    if ( minorID < _tableCount && _table[minorID].isAssigned )
3348        return &_table[minorID];
3349    else
3350        return 0;
3351}
3352
3353void MinorTable::obsolete(UInt32 minorID)
3354{
3355    //
3356    // This method obsoletes the specified minor, that is, the slot is marked
3357    // as obsolete and will be removed later via the dkclose function once it
3358    // detects the last close arrive.  Once obsoleted, the minor can be cons-
3359    // idered to be removed, since it will not appear in locate searches.
3360    //
3361
3362    assert(minorID < _tableCount);
3363    assert(_table[minorID].isAssigned);
3364
3365    // Mark the minor as obsolete so that it can be removed from the table as
3366    // soon as the last close arrives (dkclose function's responsibility).
3367
3368    _table[minorID].isObsolete = true;
3369}
3370
3371bool MinorTable::isObsolete(UInt32 minorID)
3372{
3373    //
3374    // Determine whether the specified minor ID is marked as obsolete.
3375    //
3376
3377    assert(minorID < _tableCount);
3378    assert(_table[minorID].isAssigned);
3379
3380    return _table[minorID].isObsolete ? true : false;
3381}
3382
3383// =============================================================================
3384// IOMediaBSDClientGlobals Class
3385
3386static int devsw_add(int index, struct bdevsw * bsw, struct cdevsw * csw)
3387{
3388    for ( index = bdevsw_isfree(index); index != -1; index++, index = bdevsw_isfree(-index) )
3389    {
3390        int bdevsw_index;
3391
3392        bdevsw_index = bdevsw_add(index, bsw);
3393
3394        if (bdevsw_index == index)
3395        {
3396            int cdevsw_index;
3397
3398            cdevsw_index = cdevsw_add_with_bdev(index, csw, index);
3399
3400            if (cdevsw_index == index)
3401            {
3402                break;
3403            }
3404
3405            bdevsw_remove(bdevsw_index, bsw);
3406        }
3407    }
3408
3409    return index;
3410}
3411
3412static int devsw_remove(int index, struct bdevsw * bsw, struct cdevsw * csw)
3413{
3414    index = bdevsw_remove(index, bsw);
3415
3416    if (index != -1)
3417    {
3418        index = cdevsw_remove(index, csw);
3419    }
3420
3421    return index;
3422}
3423
3424IOMediaBSDClientGlobals::IOMediaBSDClientGlobals()
3425{
3426    //
3427    // Initialize the minimal global state.
3428    //
3429
3430    _anchors   = new AnchorTable();
3431    _minors    = new MinorTable();
3432
3433    _majorID   = devsw_add(-1, &bdevswFunctions, &cdevswFunctions);
3434
3435    _openLock  = IOLockAlloc();
3436    _stateLock = IOLockAlloc();
3437///w:start
3438#if !TARGET_OS_EMBEDDED
3439    _assertionCall = thread_call_allocate(dkreadwriteassertion, NULL);
3440    _assertionID   = kIOPMUndefinedDriverAssertionID;
3441    _assertionLock = IOLockAlloc();
3442#endif /* !TARGET_OS_EMBEDDED */
3443///w:stop
3444}
3445
3446IOMediaBSDClientGlobals::~IOMediaBSDClientGlobals()
3447{
3448    //
3449    // Free all of the outstanding global resources.
3450    //
3451
3452///w:start
3453#if !TARGET_OS_EMBEDDED
3454    if ( _assertionCall )               thread_call_free(_assertionCall);
3455    if ( _assertionLock )               IOLockFree(_assertionLock);
3456#endif /* !TARGET_OS_EMBEDDED */
3457///w:stop
3458    if ( _openLock )                    IOLockFree(_openLock);
3459    if ( _stateLock )                   IOLockFree(_stateLock);
3460
3461    if ( _majorID != kInvalidMajorID )  devsw_remove(_majorID, &bdevswFunctions, &cdevswFunctions);
3462
3463    if ( _minors )                      delete _minors;
3464    if ( _anchors )                     delete _anchors;
3465}
3466
3467AnchorTable * IOMediaBSDClientGlobals::getAnchors()
3468{
3469    //
3470    // Obtain the table of anchors.
3471    //
3472
3473    return _anchors;
3474}
3475
3476MinorTable * IOMediaBSDClientGlobals::getMinors()
3477{
3478    //
3479    // Obtain the table of minors.
3480    //
3481
3482    return _minors;
3483}
3484
3485MinorSlot * IOMediaBSDClientGlobals::getMinor(UInt32 minorID)
3486{
3487    //
3488    // Obtain information for the specified minor ID.
3489    //
3490
3491    return _minors->getMinor(minorID);
3492}
3493
3494UInt32 IOMediaBSDClientGlobals::getMajorID()
3495{
3496    //
3497    // Obtain the major ID.
3498    //
3499
3500    return _majorID;
3501}
3502
3503bool IOMediaBSDClientGlobals::isValid()
3504{
3505    //
3506    // Determine whether the minimal global state has been initialized.
3507    //
3508
3509    return ( _anchors                    ) &&
3510           ( _minors                     ) &&
3511           ( _majorID != kInvalidMajorID ) &&
3512///w:start
3513#if !TARGET_OS_EMBEDDED
3514           ( _assertionCall              ) &&
3515           ( _assertionLock              ) &&
3516#endif /* !TARGET_OS_EMBEDDED */
3517///w:stop
3518           ( _openLock                   ) &&
3519           ( _stateLock                  );
3520}
3521
3522void IOMediaBSDClientGlobals::lockOpen()
3523{
3524    //
3525    // Disable access to the opens and closes.
3526    //
3527
3528    IOLockLock(_openLock);
3529}
3530
3531void IOMediaBSDClientGlobals::unlockOpen()
3532{
3533    //
3534    // Enable access to the opens and closes.
3535    //
3536
3537    IOLockUnlock(_openLock);
3538}
3539
3540void IOMediaBSDClientGlobals::lockState()
3541{
3542    //
3543    // Disable access to the global state.
3544    //
3545
3546    IOLockLock(_stateLock);
3547}
3548
3549void IOMediaBSDClientGlobals::unlockState()
3550{
3551    //
3552    // Enable access to the global state.
3553    //
3554
3555    IOLockUnlock(_stateLock);
3556}
3557///w:start
3558#if !TARGET_OS_EMBEDDED
3559thread_call_t IOMediaBSDClientGlobals::getAssertionCall()
3560{
3561    return _assertionCall;
3562}
3563
3564IOPMDriverAssertionID IOMediaBSDClientGlobals::getAssertionID()
3565{
3566    return _assertionID;
3567}
3568
3569void IOMediaBSDClientGlobals::setAssertionID(IOPMDriverAssertionID assertionID)
3570{
3571    _assertionID = assertionID;
3572}
3573
3574AbsoluteTime IOMediaBSDClientGlobals::getAssertionTime()
3575{
3576    return _assertionTime;
3577}
3578
3579void IOMediaBSDClientGlobals::setAssertionTime(AbsoluteTime assertionTime)
3580{
3581    _assertionTime = assertionTime;
3582}
3583
3584void IOMediaBSDClientGlobals::lockAssertion()
3585{
3586    IOLockLock(_assertionLock);
3587}
3588
3589void IOMediaBSDClientGlobals::unlockAssertion()
3590{
3591    IOLockUnlock(_assertionLock);
3592}
3593#endif /* !TARGET_OS_EMBEDDED */
3594///w:stop
3595