Annotation of XNU/iokit/Families/IOStorage/IOMediaBSDClient.cpp, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
        !             3:  *
        !             4:  * @APPLE_LICENSE_HEADER_START@
        !             5:  * 
        !             6:  * The contents of this file constitute Original Code as defined in and
        !             7:  * are subject to the Apple Public Source License Version 1.1 (the
        !             8:  * "License").  You may not use this file except in compliance with the
        !             9:  * License.  Please obtain a copy of the License at
        !            10:  * http://www.apple.com/publicsource and read it before using this file.
        !            11:  * 
        !            12:  * This Original Code and all software distributed under the License are
        !            13:  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
        !            14:  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
        !            15:  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
        !            16:  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
        !            17:  * License for the specific language governing rights and limitations
        !            18:  * under the License.
        !            19:  * 
        !            20:  * @APPLE_LICENSE_HEADER_END@
        !            21:  */
        !            22: 
        !            23: #include <dev/disk.h>                        // (DKIOCGETBLOCKSIZE, ...)
        !            24: #include <mach/vm_types.h>                   // (mach/vm_region.h, ...)
        !            25: #include <mach/vm_region.h>                  // (VM_REGION_BASIC_INFO, ...)
        !            26: #include <miscfs/devfs/devfs.h>              // (devfs_make_node, ...)
        !            27: #include <sys/buf.h>                         // (struct buf, ...)
        !            28: #include <sys/conf.h>                        // (bdevsw_add, ...)
        !            29: #include <sys/fcntl.h>                       // (FWRITE, ...)
        !            30: #include <sys/ioccom.h>                      // (IOCGROUP, ...)
        !            31: #include <sys/stat.h>                        // (S_ISBLK, ...)
        !            32: #include <sys/uio.h>                         // (struct uio, ...)
        !            33: #include <IOKit/assert.h>
        !            34: #include <IOKit/IOBSD.h>
        !            35: #include <IOKit/IODeviceTreeSupport.h>
        !            36: #include <IOKit/IOLib.h>
        !            37: #include <IOKit/IOMemoryDescriptor.h>
        !            38: #include <IOKit/IOMessage.h>
        !            39: #include <IOKit/storage/IODrive.h>
        !            40: #include <IOKit/storage/IOMedia.h>
        !            41: #include <IOKit/storage/IOMediaBSDClient.h>
        !            42: 
        !            43: #define super IOService
        !            44: OSDefineMetaClassAndStructors(IOMediaBSDClient, IOService)
        !            45: 
        !            46: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !            47: 
        !            48: static IOMediaBSDClient * gIOMediaBSDClient = 0;
        !            49: 
        !            50: const   signed kMajor            = 14;       // (bsd interface [b|c]devsw major)
        !            51: const unsigned kMinorsGrowCount  = 16;       // (entries to add on table growth)
        !            52: const unsigned kMinorsMaxCount   = 1 << 24;  // (maximum entries;  24-bit minor)
        !            53: const unsigned kAnchorsGrowCount = 2;        // (entries to add on table growth)
        !            54: const unsigned kAnchorsMaxCount  = kMinorsMaxCount;         // (maximum entries)
        !            55: 
        !            56: #define kMsgBadWhole   "%s: Peer whole media \"%s\" is not allowed.",  getName()
        !            57: #define kMsgNoWhole    "%s: No whole media found for media \"%s\".\n", getName()
        !            58: #define kMsgNoLocation "%s: No location is found for media \"%s\".\n", getName()
        !            59: 
        !            60: #define IOMEDIABSDCLIENT_IOSTAT_SUPPORT       // (enable iostat support for bsd)
        !            61: 
        !            62: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !            63: 
        !            64: extern "C"
        !            65: {
        !            66:     int  dkclose(dev_t dev, int flags, int devtype, struct proc *);
        !            67:     int  dkioctl(dev_t dev, u_long cmd, caddr_t data, int, struct proc *);
        !            68:     int  dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int, struct proc *);
        !            69:     int  dkopen(dev_t dev, int flags, int devtype, struct proc *);
        !            70:     int  dkread(dev_t dev, struct uio * uio, int flags);
        !            71:     int  dksize(dev_t dev);    
        !            72:     void dkstrategy(struct buf * bp);
        !            73:     int  dkwrite(dev_t dev, struct uio * uio, int flags);
        !            74: } // extern "C"
        !            75: 
        !            76: static struct bdevsw bdevswFunctions =
        !            77: {
        !            78:     /* d_open     */ dkopen,
        !            79:     /* d_close    */ dkclose,
        !            80:     /* d_strategy */ dkstrategy,
        !            81:     /* d_ioctl    */ dkioctl_bdev,
        !            82:     /* d_dump     */ eno_dump,
        !            83:     /* d_psize    */ dksize,
        !            84:     /* d_type     */ D_DISK
        !            85: };
        !            86: 
        !            87: struct cdevsw cdevswFunctions =
        !            88: {
        !            89:     /* d_open     */ dkopen,
        !            90:     /* d_close    */ dkclose,
        !            91:     /* d_read     */ dkread,
        !            92:     /* d_write    */ dkwrite,
        !            93:     /* d_ioctl    */ dkioctl,
        !            94:     /* d_stop     */ eno_stop,
        !            95:     /* d_reset    */ eno_reset,
        !            96:     /* d_ttys     */ 0,
        !            97:     /* d_select   */ eno_select,
        !            98:     /* d_mmap     */ eno_mmap,
        !            99:     /* d_strategy */ eno_strat,
        !           100:     /* d_getc     */ eno_getc,
        !           101:     /* d_putc     */ eno_putc,
        !           102:     /* d_type     */ D_TAPE
        !           103: };
        !           104: 
        !           105: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           106: 
        !           107: struct dio { dev_t dev; struct uio * uio; };
        !           108: 
        !           109: typedef void *                            dkr_t;       /* dkreadwrite request */
        !           110: typedef enum { DKRTYPE_BUF, DKRTYPE_DIO } dkrtype_t;
        !           111: 
        !           112: int  dkreadwrite(dkr_t dkr, dkrtype_t dkrtype);
        !           113: void dkreadwritecompletion(void *, void *, IOReturn, UInt64);
        !           114: 
        !           115: #define get_kernel_task() kernel_task
        !           116: #define get_user_task()   current_task()
        !           117: 
        !           118: #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
        !           119: #include <sys/dkstat.h>
        !           120: IODrive * dk_drive[DK_NDRIVE];
        !           121: #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
        !           122: 
        !           123: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           124: 
        !           125: const UInt32 kInvalidAnchorID = (UInt32) (-1);
        !           126: 
        !           127: struct AnchorSlot
        !           128: {
        !           129:     UInt32       isAssigned:1, // (anchor slot is occupied)
        !           130:                  isObsolete:1; // (anchor slot is to be removed once refs gone)
        !           131: 
        !           132:     IOService *  anchor;       // (anchor object)
        !           133:     IONotifier * notifier;     // (anchor termination notification, post-stop)
        !           134: };
        !           135: 
        !           136: class AnchorTable
        !           137: {
        !           138: protected:
        !           139:     AnchorSlot * _table;
        !           140:     UInt32       _tableCount;
        !           141:     UInt32       _tableGrowCount;
        !           142:     UInt32       _tableMaxCount;
        !           143: 
        !           144:     static IOReturn anchorChange(void *, void *, UInt32,
        !           145:                                  IOService *, void *, vm_size_t);
        !           146: 
        !           147: public:
        !           148:     AnchorTable(UInt32 growCount, UInt32 maxCount);
        !           149:     ~AnchorTable();
        !           150: 
        !           151:     UInt32 insert(IOService * anchor);
        !           152:     UInt32 locate(IOService * anchor);
        !           153:     void   obsolete(UInt32 anchorID);
        !           154:     void   remove(UInt32 anchorID);
        !           155: 
        !           156:     bool   isObsolete(UInt32 anchorID);
        !           157: };
        !           158: 
        !           159: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           160: 
        !           161: const UInt32 kInvalidMinorID = (UInt32) (-1);
        !           162: 
        !           163: struct MinorSlot
        !           164: {
        !           165:     UInt32       isAssigned:1,  // (minor slot is occupied)
        !           166:                  isEjecting:1,  // (minor slot is in eject flux, needs close)
        !           167:                  isObsolete:1;  // (minor slot is in eject flux, needs removal)
        !           168: 
        !           169:     UInt32       anchorID;      // (minor's associated anchor ID)
        !           170:     IOMedia *    media;         // (minor's media object)
        !           171:     char *       name;          // (minor's name, private allocation space)
        !           172: 
        !           173:     UInt64       bdevBlockSize; // (block device's preferred block size)
        !           174:     void *       bdevNode;      // (block device's devfs node)
        !           175:     UInt32       bdevOpen:1,    // (block device's open flag)
        !           176:                  bdevWriter:1;  // (block device's open writer flag)
        !           177: 
        !           178:     void *       cdevNode;      // (character device's devfs node)
        !           179:     UInt32       cdevOpen:1,    // (character device's open flag)
        !           180:                  cdevWriter:1;  // (character device's open writer flag)
        !           181: };
        !           182: 
        !           183: class MinorTable
        !           184: {
        !           185: protected:
        !           186:     MinorSlot * _table;
        !           187:     UInt32      _tableCount;
        !           188:     UInt32      _tableGrowCount;
        !           189:     UInt32      _tableMaxCount;
        !           190: 
        !           191: public:
        !           192:     MinorTable(UInt32 growCount, UInt32 maxCount);
        !           193:     ~MinorTable();
        !           194: 
        !           195:     UInt32      insert(IOMedia * media, UInt32 anchorID, char * slicePath);
        !           196:     UInt32      locate(IOMedia * media);
        !           197:     void        obsolete(UInt32 minorID);
        !           198:     void        remove(UInt32 minorID);
        !           199: 
        !           200:     bool        isObsolete(UInt32 minorID);
        !           201: 
        !           202:     MinorSlot * getMinor(UInt32 minorID);
        !           203: 
        !           204:     UInt32      getOpenCountForAnchorID(UInt32 anchorID);
        !           205:     IOMedia *   getWholeMediaAtAnchorID(UInt32 anchorID);
        !           206:     bool        hasReferencesToAnchorID(UInt32 anchorID);
        !           207: };
        !           208: 
        !           209: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           210: 
        !           211: bool IOMediaBSDClient::init(OSDictionary * properties = 0)
        !           212: {
        !           213:     //
        !           214:     // Initialize this object's minimal state.
        !           215:     //
        !           216: 
        !           217:     if ( super::init(properties) == false )  return false;
        !           218: 
        !           219:     _anchors         = new AnchorTable(kAnchorsGrowCount, kAnchorsMaxCount);
        !           220:     _bdevswInstalled = false;
        !           221:     _cdevswInstalled = false;
        !           222:     _minors          = new MinorTable(kMinorsGrowCount, kMinorsMaxCount);
        !           223:     _notifier        = 0;
        !           224: 
        !           225:     if ( _anchors == 0 || _minors == 0 )  return false;
        !           226: 
        !           227:     return true;
        !           228: }
        !           229: 
        !           230: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           231: 
        !           232: void IOMediaBSDClient::free()
        !           233: {
        !           234:     //
        !           235:     // Free all of this object's outstanding resources.
        !           236:     //
        !           237: 
        !           238:     if ( _notifier )         _notifier->remove();
        !           239:     if ( _cdevswInstalled )  cdevsw_remove(kMajor, &cdevswFunctions); 
        !           240:     if ( _bdevswInstalled )  bdevsw_remove(kMajor, &bdevswFunctions);
        !           241:     if ( _minors )           delete _minors;
        !           242:     if ( _anchors )          delete _anchors;
        !           243: 
        !           244:     super::free();
        !           245: }
        !           246: 
        !           247: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           248: 
        !           249: bool IOMediaBSDClient::start(IOService * provider)
        !           250: {
        !           251:     //
        !           252:     // This method is called once we have been attached to the provider object.
        !           253:     //
        !           254: 
        !           255:     assert(gIOMediaBSDClient == 0);
        !           256: 
        !           257:     // Ask our superclass' opinion.
        !           258: 
        !           259:     if ( super::start(provider) == false )  return false;
        !           260: 
        !           261:     // Establish a global reference to this instance.
        !           262: 
        !           263:     gIOMediaBSDClient = this;
        !           264: 
        !           265:     // Install bdevsw and cdevsw functions.
        !           266: 
        !           267:     _bdevswInstalled = (bdevsw_add(kMajor, &bdevswFunctions) == kMajor);
        !           268:     _cdevswInstalled = (cdevsw_add(kMajor, &cdevswFunctions) == kMajor);
        !           269: 
        !           270:     if ( _bdevswInstalled == false && _cdevswInstalled == false )  return false;
        !           271: 
        !           272:     // Create a notification handler for media arrival.  We ask for a priority
        !           273:     // of ten to ensure that we are notified ahead of other interested clients
        !           274:     // (with a default priority of zero), so that we can place the BSD-related
        !           275:     // properties on the media object that they might need in time.
        !           276: 
        !           277:     _notifier = addNotification( /* type        */ gIOFirstPublishNotification,
        !           278:                                  /* description */ serviceMatching("IOMedia"),
        !           279:                                  /* action      */ mediaHasArrived,
        !           280:                                  /* target      */ this,
        !           281:                                  /* parameter   */ 0,
        !           282:                                  /* priority    */ 10 );
        !           283: 
        !           284:     if ( _notifier == 0 )  return false;
        !           285: 
        !           286:     // Register this object so it can be found via notification requests. It is
        !           287:     // not being registered to have I/O Kit attempt to have drivers match on it,
        !           288:     // which is the reason most other services are registered -- that's not the
        !           289:     // intention of this registerService call.
        !           290: 
        !           291:     registerService();
        !           292: 
        !           293:     return true;
        !           294: }
        !           295: 
        !           296: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           297: 
        !           298: void IOMediaBSDClient::stop(IOService * provider)
        !           299: {
        !           300:     //
        !           301:     // This method is called before we are detached from the provider object.
        !           302:     //
        !           303: 
        !           304:     IOMedia * media   = (IOMedia *) provider;
        !           305:     UInt32    minorID = 0;
        !           306: 
        !           307:     // Disable access to tables, matching, opens, closes, and terminations.
        !           308: 
        !           309:     gIOMediaBSDClient->lockForArbitration();
        !           310: 
        !           311:     // Find the minor assigned to this media.
        !           312: 
        !           313:     minorID = _minors->locate(media);
        !           314:     assert(minorID != kInvalidMinorID);
        !           315: 
        !           316:     // State our assumptions.
        !           317: 
        !           318:     assert(media->isOpen() == false);
        !           319: 
        !           320:     // Remove the minor from the minor table, unless it's still in flux (which
        !           321:     // means an open on the bdevsw/cdevsw switch is still outstanding: the one
        !           322:     // that sent the eject ioctl), in which case we mark the minor as obsolete
        !           323:     // for later removal.
        !           324: 
        !           325:     if ( _minors->getMinor(minorID)->isEjecting )         // (is minor in flux?)
        !           326:     {
        !           327:         assert(_minors->isObsolete(minorID) == false);
        !           328: 
        !           329:         _minors->obsolete(minorID);
        !           330:     }
        !           331:     else
        !           332:     {
        !           333:         assert(_minors->getMinor(minorID)->bdevOpen == false);
        !           334:         assert(_minors->getMinor(minorID)->cdevOpen == false);
        !           335: 
        !           336:         _minors->remove(minorID);
        !           337:     }
        !           338:    
        !           339:     // Enable access to tables, matching, opens, closes, and terminations.
        !           340: 
        !           341:     gIOMediaBSDClient->unlockForArbitration();
        !           342: 
        !           343:     // Call upon the superclass to finish its work.
        !           344: 
        !           345:     super::stop(media);
        !           346: }
        !           347: 
        !           348: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           349: 
        !           350: bool IOMediaBSDClient::mediaHasArrived( void *      /* target */,
        !           351:                                         void *      /* parameter */,
        !           352:                                         IOService * service )
        !           353: {
        !           354:     //
        !           355:     // Notification handler for media arrivals.
        !           356:     //
        !           357: 
        !           358:     IOMedia * media   = OSDynamicCast(IOMedia, service);
        !           359:     bool      success = false;
        !           360: 
        !           361:     assert(gIOMediaBSDClient);
        !           362: 
        !           363:     // Attach the media-bsd-client object as a client of the new media object.
        !           364: 
        !           365:     if ( media && gIOMediaBSDClient->attach(media) )
        !           366:     {
        !           367:         // Disable access to tables, matching, opens, closes, and terminations.
        !           368: 
        !           369:         gIOMediaBSDClient->lockForArbitration();
        !           370: 
        !           371:         // Create bdevsw and cdevsw nodes for the new media object.
        !           372: 
        !           373:         success = gIOMediaBSDClient->createNodes(media);
        !           374: 
        !           375:         // Enable access to tables, matching, opens, closes, and terminations.
        !           376: 
        !           377:         gIOMediaBSDClient->unlockForArbitration();
        !           378: 
        !           379:         // Detach the media-bsd-client object from the media object on error.
        !           380: 
        !           381:         if (success == false)  gIOMediaBSDClient->detach(media);
        !           382:     }
        !           383: 
        !           384:     return true; // (meaningless return value)
        !           385: }
        !           386: 
        !           387: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           388: 
        !           389: IOMedia * IOMediaBSDClient::getWholeMedia( IOMedia * media,
        !           390:                                            UInt32 *  slicePathSize = 0,
        !           391:                                            char *    slicePath     = 0 )
        !           392: {
        !           393:     //
        !           394:     // Find the whole media that roots this media tree.  A null return value
        !           395:     // indicates no whole media was found or a malformed tree was detected.
        !           396:     //
        !           397:     // If slicePathSize is non-zero, the size required to fit the slice path
        !           398:     // (including the zero terminator) is passed back as a result.
        !           399:     //
        !           400:     // If slicePathSize and slicePath are both non-zero, the slice path will
        !           401:     // be written into the slicePath buffer.  The value slicePathSize points
        !           402:     // to must be the size of the slicePath buffer, which is used for sanity
        !           403:     // checking in this method.
        !           404:     //
        !           405: 
        !           406:     UInt32      depth    = 1;
        !           407:     UInt32      position = sizeof('\0');
        !           408:     IOService * service  = 0;
        !           409: 
        !           410:     assert(slicePath == 0 || slicePathSize != 0);
        !           411: 
        !           412:     // Search the registry for the parent whole media for this media.
        !           413: 
        !           414:     for ( service = media; service; service = service->getProvider() )
        !           415:     {
        !           416:         if ( OSDynamicCast(IOMedia, service) )               // (is it a media?)
        !           417:         {
        !           418:             if ( ((IOMedia *)service)->isWhole() )     // (is it a whole media?)
        !           419:             {
        !           420:                 if ( slicePath )            // (are we building the slice path?)
        !           421:                 {
        !           422:                     slicePath[*slicePathSize - 1] = 0;  // (zero terminate path)
        !           423: 
        !           424:                     if ( position < *slicePathSize )     // (need to move path?)
        !           425:                     {
        !           426:                         memmove( slicePath,    // (move path to start of buffer)
        !           427:                                  slicePath + (*slicePathSize - position),
        !           428:                                  position );
        !           429:                     }
        !           430:                 }
        !           431:                 else if ( slicePathSize ) // (report size req'd for slice path?)
        !           432:                 {
        !           433:                     *slicePathSize = position;
        !           434:                 }
        !           435: 
        !           436:                 return (IOMedia *)service;           // (return the whole media)
        !           437:             }
        !           438: 
        !           439:             // Determine whether this non-whole media has a location value.  It
        !           440:             // must, by definition of a non-whole media, but if it does not, we
        !           441:             // should return an error condition.
        !           442: 
        !           443:             const char * location = service->getLocation();
        !           444: 
        !           445:             if ( location == 0 )            // (no location on non-whole media?)
        !           446:             {
        !           447:                 if ( service == media ) IOLog(kMsgNoLocation, media->getName());
        !           448:                 return 0;
        !           449:             }
        !           450: 
        !           451:             // Otherwise, it's a valid non-whole media: we compute the required
        !           452:             // size for the slice path or build the slice path, if so requested.
        !           453:             // Note that the slice path is built backwards from the ends of the
        !           454:             // supplied buffer to the beginning of the buffer.
        !           455: 
        !           456:             position += sizeof('s') + strlen(location);
        !           457: 
        !           458:             if ( slicePath )                          // (build the slice path?)
        !           459:             {
        !           460:                 char * path = slicePath + *slicePathSize - position;
        !           461: 
        !           462:                 if ( position > *slicePathSize )  { assert(0);  return 0; }
        !           463: 
        !           464:                 *path = 's';
        !           465:                 strncpy(path + sizeof('s'), location, strlen(location));
        !           466:             }
        !           467: 
        !           468:             depth += 1;
        !           469:         }
        !           470:     }
        !           471: 
        !           472:     // If we've fallen through, then the whole media was never found.
        !           473: 
        !           474:     if ( depth == 1 ) IOLog(kMsgNoWhole, media->getName());
        !           475:     return 0;
        !           476: }
        !           477: 
        !           478: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           479: 
        !           480: bool IOMediaBSDClient::createNodes(IOMedia * media)
        !           481: {
        !           482:     //
        !           483:     // Create bdevsw and cdevsw nodes for the given media object.
        !           484:     //
        !           485:     // This method assumes that the arbitration lock is held.
        !           486:     //
        !           487: 
        !           488:     IOService * anchor;
        !           489:     UInt32      anchorID;
        !           490:     bool        anchorNew = false;
        !           491:     UInt32      minorID;
        !           492:     char *      slicePath = 0;
        !           493:     UInt32      slicePathSize;
        !           494:     IOMedia *   whole;
        !           495: 
        !           496:     //
        !           497:     // Find the anchor that roots this media tree.  The anchor is defined as the
        !           498:     // parent of the whole media that roots this media tree.  It is an important
        !           499:     // object to us because this object stays in place when media is ejected, so
        !           500:     // we can continue to maintain the "unit number" of the "drive" such that if
        !           501:     // media is re-inserted, it will show up under the same "unit number".   You
        !           502:     // can think of the typical anchor as being the drive, if it helps, although
        !           503:     // it could be one of many other kinds of objects (eg. a RAID scheme).
        !           504:     //
        !           505: 
        !           506:     whole = getWholeMedia(media, &slicePathSize);
        !           507:     if ( whole == 0 )  return false;
        !           508: 
        !           509:     anchor = whole->getProvider();
        !           510:     if ( anchor == 0 )  return false;
        !           511: 
        !           512:     //
        !           513:     // Determine whether the anchor already exists in the anchor table (obsolete
        !           514:     // occurences are skipped in the search, as appropriate,  since those anchor
        !           515:     // IDs are to be removed soon). If the anchor does not exist, insert it into
        !           516:     // anchor table.
        !           517:     //
        !           518: 
        !           519:     anchorID = _anchors->locate(anchor);
        !           520: 
        !           521:     if ( anchorID != kInvalidAnchorID )
        !           522:     {
        !           523:         //
        !           524:         // The anchor does exist in the table, however we've got more to check.
        !           525:         //
        !           526:         // We need to ensure that the whole media associated with this anchor is
        !           527:         // the same as ours.  If it is, all is well.  If it isn't, then there is
        !           528:         // still a chance all is well.  It is possible to have an old media tree
        !           529:         // still associated with the anchor: the tree would be inactive, but not
        !           530:         // yet terminated (this can happen on forced termination of a media tree
        !           531:         // with oustanding opens, since the close must come before the terminate
        !           532:         // can proceed; it can happen even in normal eject conditions should the
        !           533:         // media be immediately reinserted when the termination on the old tree,
        !           534:         // which is asynchronous, is still chugging along on another thread). In
        !           535:         // case the tree is inactive, we mark the anchorID as obsolete and use a
        !           536:         // new anchorID.  In the case the tree is not inactive, then we've got a
        !           537:         // problem and we must bail out.
        !           538:         //
        !           539:         // A few additional notes:
        !           540:         //
        !           541:         //  o if the whole media is indeed the same as the one in our tables, we
        !           542:         //    need not check that it is active, because by virtue of the fact we
        !           543:         //    got a new media notification on the same tree, we know for sure it
        !           544:         //    cannot be in the inactive state.
        !           545:         //
        !           546:         //  o if the whole media is not in our tables, it is quite possible that
        !           547:         //    some child non-whole media from the old media tree is still around
        !           548:         //    as terminations work from the bottom (whole media) up (to leaves),
        !           549:         //    and the asynchronous termination thread is still not done chugging
        !           550:         //    through the medias on the old tree.  We use a new anchorID in this
        !           551:         //    case.
        !           552:         //
        !           553: 
        !           554:         IOMedia * wholeInTable = _minors->getWholeMediaAtAnchorID(anchorID);
        !           555: 
        !           556:         if ( wholeInTable == 0 )  // (is an existing whole media in our tables?)
        !           557:         {
        !           558:             if ( _minors->hasReferencesToAnchorID(anchorID) )   // (any medias?)
        !           559:             {
        !           560:                 _anchors->obsolete(anchorID);        // (obsolete old anchor ID)
        !           561:                 anchorID = kInvalidAnchorID;         // ( request new anchor ID)
        !           562:             }                                             // (else, all is well)
        !           563:         }
        !           564:         else if ( whole != wholeInTable )  // (old whole media not same as new?)
        !           565:         {
        !           566:             if ( wholeInTable->isInactive() )   // (is it inactive/terminating?)
        !           567:             {
        !           568:                 _anchors->obsolete(anchorID);        // (obsolete old anchor ID)
        !           569:                 anchorID = kInvalidAnchorID;         // ( request new anchor ID)
        !           570:             }
        !           571:             else               // (peer active whole medias detected, log error)
        !           572:             {
        !           573:                 if ( whole == media )  IOLog(kMsgBadWhole, whole->getName());
        !           574:                 return false;
        !           575:             }
        !           576:         }                                                 // (else, all is well)
        !           577:     }
        !           578: 
        !           579:     if ( anchorID == kInvalidAnchorID )
        !           580:     {
        !           581:         anchorID = _anchors->insert(anchor);              // (get new anchor ID)
        !           582:         if ( anchorID == kInvalidAnchorID )  return false;
        !           583:         anchorNew = true;
        !           584:     }
        !           585: 
        !           586:     //
        !           587:     // Allocate space for and build the slice path for the device node names.
        !           588:     //
        !           589: 
        !           590:     slicePath = (char *) IOMalloc(slicePathSize);
        !           591:     if ( slicePath == 0 )  goto createNodesErr;
        !           592: 
        !           593:     whole = getWholeMedia(media, &slicePathSize, slicePath);
        !           594:     assert(whole);
        !           595: 
        !           596:     //
        !           597:     // Insert the new media into our minor table (we're almost done :-).
        !           598:     //
        !           599: 
        !           600:     minorID = _minors->insert(media, anchorID, slicePath);
        !           601:     if ( minorID == kInvalidMinorID )  goto createNodesErr;
        !           602: 
        !           603:     //
        !           604:     // Create the required properties on the media.
        !           605:     //
        !           606: 
        !           607:     media->setProperty(kIOBSDName,  _minors->getMinor(minorID)->name);
        !           608:     media->setProperty(kIOBSDUnit,  anchorID, 32);              // ("BSD Unit" )
        !           609:     media->setProperty(kIOBSDMajor, kMajor,   32);              // ("BSD Major")
        !           610:     media->setProperty(kIOBSDMinor, minorID,  32);              // ("BSD Minor")
        !           611: 
        !           612:     //
        !           613:     // Clean up outstanding resources.
        !           614:     //
        !           615: 
        !           616:     IOFree(slicePath, slicePathSize);
        !           617: 
        !           618:     return true; // (success)
        !           619: 
        !           620: createNodesErr:
        !           621: 
        !           622:     if (anchorNew)  _anchors->remove(anchorID);
        !           623:     if (slicePath)  IOFree(slicePath, slicePathSize);
        !           624: 
        !           625:     return false; // (failure)
        !           626: }
        !           627: 
        !           628: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           629: 
        !           630: AnchorTable * IOMediaBSDClient::getAnchors()
        !           631: {
        !           632:     //
        !           633:     // Obtain the table of anchors.
        !           634:     //
        !           635: 
        !           636:     return _anchors;
        !           637: }
        !           638: 
        !           639: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           640: 
        !           641: MinorTable * IOMediaBSDClient::getMinors()
        !           642: {
        !           643:     //
        !           644:     // Obtain the table of anchors.
        !           645:     //
        !           646: 
        !           647:     return _minors;
        !           648: }
        !           649: 
        !           650: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           651: 
        !           652: MinorSlot * IOMediaBSDClient::getMinor(UInt32 minorID)
        !           653: {
        !           654:     //
        !           655:     // Obtain information for the specified minor ID.
        !           656:     //
        !           657: 
        !           658:     return _minors->getMinor(minorID);
        !           659: }
        !           660: 
        !           661: // =============================================================================
        !           662: // BSD Functions
        !           663: 
        !           664: int dkopen(dev_t dev, int flags, int devtype, struct proc *)
        !           665: {
        !           666:     //
        !           667:     // dkopen opens the device (called on each open).
        !           668:     //
        !           669: 
        !           670:     int             error;
        !           671:     IOStorageAccess level;
        !           672:     MinorSlot *     minor;
        !           673: 
        !           674:     assert(gIOMediaBSDClient);
        !           675:     assert(S_ISBLK(devtype) || S_ISCHR(devtype));
        !           676: 
        !           677:     gIOMediaBSDClient->lockForArbitration();                 // (disable access)
        !           678: 
        !           679:     assert(gIOMediaBSDClient->getMinors());
        !           680: 
        !           681:     error = 0;
        !           682:     level = (flags & FWRITE) ? kAccessReaderWriter : kAccessReader;
        !           683:     minor = gIOMediaBSDClient->getMinor(minor(dev));
        !           684: 
        !           685:     //
        !           686:     // Process the open.
        !           687:     //
        !           688: 
        !           689:     if ( minor == 0 )                                       // (is minor valid?)
        !           690:     {
        !           691:         error = ENXIO;
        !           692:     }
        !           693:     else if ( minor->isEjecting )                         // (is minor in flux?)
        !           694:     {
        !           695:         error = EBUSY;
        !           696:     }
        !           697:     else if ( (flags & FWRITE) )                        // (is client a writer?)
        !           698:     {
        !           699:         if ( minor->bdevWriter || minor->cdevWriter )  level = kAccessNone;
        !           700:     }
        !           701:     else                                                // (is client a reader?)
        !           702:     {
        !           703:         if ( minor->bdevOpen || minor->cdevOpen )  level = kAccessNone;
        !           704:     }
        !           705: 
        !           706:     if ( error == 0 && level != kAccessNone )  // (issue the open (or upgrade)?)
        !           707:     {
        !           708:         if ( minor->media->open(gIOMediaBSDClient, 0, level) == false )  // (go)
        !           709:         {
        !           710:             error = EBUSY;
        !           711:         }
        !           712:     }
        !           713: 
        !           714:     if ( error == 0 )                                          // (update state)
        !           715:     {
        !           716:         if ( S_ISBLK(devtype) )
        !           717:         {
        !           718:             minor->bdevOpen = true;
        !           719:             if ( (flags & FWRITE) )  minor->bdevWriter = true;
        !           720:         }
        !           721:         else
        !           722:         {
        !           723:             minor->cdevOpen = true;
        !           724:             if ( (flags & FWRITE) )  minor->cdevWriter = true;
        !           725:         }
        !           726:     }
        !           727: 
        !           728:     gIOMediaBSDClient->unlockForArbitration();                // (enable access)
        !           729: 
        !           730:     return error;
        !           731: }
        !           732: 
        !           733: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           734: 
        !           735: int dkclose(dev_t dev, int /* flags */, int devtype, struct proc *)
        !           736: {
        !           737:     //
        !           738:     // dkclose closes the device (called on last close).
        !           739:     //
        !           740: 
        !           741:     MinorSlot * minor;
        !           742:     bool        wasWriter;
        !           743: 
        !           744:     assert(S_ISBLK(devtype) || S_ISCHR(devtype));
        !           745: 
        !           746:     gIOMediaBSDClient->lockForArbitration();                 // (disable access)
        !           747: 
        !           748:     minor     = gIOMediaBSDClient->getMinor(minor(dev));
        !           749:     wasWriter = (minor->bdevWriter || minor->cdevWriter); 
        !           750: 
        !           751:     if ( S_ISBLK(devtype) )                                    // (update state)
        !           752:     {
        !           753:         minor->bdevBlockSize = minor->media->getPreferredBlockSize();
        !           754:         minor->bdevOpen      = false;
        !           755:         minor->bdevWriter    = false;
        !           756:     }
        !           757:     else
        !           758:     {
        !           759:         minor->cdevOpen      = false;
        !           760:         minor->cdevWriter    = false;
        !           761:     }
        !           762: 
        !           763:     if ( minor->isEjecting )                              // (is minor in flux?)
        !           764:     {
        !           765:         //
        !           766:         // We've determined that the specified minor is in ejection flux.  This
        !           767:         // means we are in a state where the media object has been closed, only
        !           768:         // the device node is still open.  This happens to the minor subsequent
        !           769:         // to a DKIOCEJECT ioctl -- this close resets the flux state to normal.
        !           770:         //
        !           771: 
        !           772:         minor->isEjecting = false;
        !           773: 
        !           774:         // If this minor is marked as obsolete, then we've already received the
        !           775:         // media's termination notification (stop method), but the minor is yet
        !           776:         // to be removed from the table -- remove it now.
        !           777: 
        !           778:         assert(minor->bdevOpen == false);
        !           779:         assert(minor->cdevOpen == false);
        !           780: 
        !           781:         if ( minor->isObsolete )
        !           782:             gIOMediaBSDClient->getMinors()->remove(minor(dev));
        !           783:     }
        !           784:     else if ( !minor->bdevOpen && !minor->cdevOpen )
        !           785:     {
        !           786:         //
        !           787:         // We communicate the close down to the media object once all opens are
        !           788:         // gone, on both the block and character device nodes.
        !           789:         //
        !           790: 
        !           791:         minor->media->close(gIOMediaBSDClient);                          // (go)
        !           792:     }
        !           793:     else if ( !minor->bdevWriter && !minor->cdevWriter && wasWriter )
        !           794:     {
        !           795:         //
        !           796:         // We communicate a downgrade down to the media object once all writers
        !           797:         // are gone and while readers still exist. 
        !           798:         //
        !           799: 
        !           800:         bool success;
        !           801:         success = minor->media->open(gIOMediaBSDClient, 0, kAccessReader);
        !           802:         assert(success);         // (should never fail, unless avoided deadlock)
        !           803:     }
        !           804: 
        !           805:     gIOMediaBSDClient->unlockForArbitration();                // (enable access)
        !           806: 
        !           807:     return 0;
        !           808: }
        !           809: 
        !           810: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           811: 
        !           812: int dkread(dev_t dev, struct uio * uio, int /* flags */)
        !           813: {
        !           814:     //
        !           815:     // dkread reads data from a device.
        !           816:     //
        !           817: 
        !           818:     struct dio dio = { dev, uio };
        !           819: 
        !           820:     return dkreadwrite(&dio, DKRTYPE_DIO);
        !           821: }
        !           822: 
        !           823: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           824: 
        !           825: int dkwrite(dev_t dev, struct uio * uio, int /* flags */)
        !           826: {
        !           827:     //
        !           828:     // dkwrite writes data to a device.
        !           829:     //
        !           830: 
        !           831:     struct dio dio = { dev, uio };
        !           832: 
        !           833:     return dkreadwrite(&dio, DKRTYPE_DIO);
        !           834: }
        !           835: 
        !           836: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           837: 
        !           838: void dkstrategy(struct buf * bp)
        !           839: {
        !           840:     //
        !           841:     // dkstrategy starts an asynchronous read or write operation.  It returns
        !           842:     // to the caller as soon as the operation is queued, and completes it via
        !           843:     // the biodone function.
        !           844:     //
        !           845: 
        !           846:     dkreadwrite(bp, DKRTYPE_BUF);
        !           847: }
        !           848: 
        !           849: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !           850: 
        !           851: int dkioctl(dev_t dev, u_long cmd, caddr_t data, int, struct proc *)
        !           852: {
        !           853:     //
        !           854:     // dkioctl performs operations other than a read or write.
        !           855:     //
        !           856: 
        !           857:     int         error = 0;
        !           858:     MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(dev));
        !           859: 
        !           860:     if ( minor->isEjecting )  return EBADF;               // (is minor in flux?)
        !           861: 
        !           862:     //
        !           863:     // Process the ioctl.
        !           864:     //
        !           865: 
        !           866:     switch ( cmd )
        !           867:     {
        !           868:         case DKIOCGETBLOCKSIZE:                      // getBlockSize(int * out);
        !           869:         {
        !           870:             //
        !           871:             // This ioctl returns the preferred block size of the media object.
        !           872:             //
        !           873: 
        !           874:             *(int *)data = (int) minor->media->getPreferredBlockSize();
        !           875: 
        !           876:         } break;
        !           877: 
        !           878:         case DKIOCGETBLOCKCOUNT:                    // getBlockCount(int * out);
        !           879:         {
        !           880:             //
        !           881:             // This ioctl returns the size of the media object in blocks.  The
        !           882:             // implied block size is returned by DKIOCGETBLOCKSIZE.
        !           883:             //
        !           884: 
        !           885:             if ( minor->media->getPreferredBlockSize() )
        !           886:                 *(int *)data = (int) ( minor->media->getSize()               / 
        !           887:                                        minor->media->getPreferredBlockSize() );
        !           888:             else
        !           889:                 *(int *)data = 0;
        !           890: 
        !           891:         } break;
        !           892: 
        !           893:         case DKIOCISFORMATTED:                        // isFormatted(int * out);
        !           894:         {
        !           895:             //
        !           896:             // This ioctl returns truth if the media object is formatted.
        !           897:             //
        !           898: 
        !           899:             *(int *)data = (int) minor->media->isFormatted();
        !           900: 
        !           901:         } break;
        !           902: 
        !           903:         case DKIOCGETLOCATION:                    // getLocation(char[128] out);
        !           904:         {
        !           905:             //
        !           906:             // This ioctl returns the open firmware path for this media object.
        !           907:             //
        !           908: 
        !           909:             int    l = sizeof(((struct drive_location *)data)->location);
        !           910:             char * p = ((struct drive_location *)data)->location;
        !           911: 
        !           912:             if ( minor->media->getPath(p, &l, gIODTPlane) && strchr(p, ':') )
        !           913:                 strcpy(p, strchr(p, ':') + 1);         // (strip the plane name)
        !           914:             else
        !           915:                 error = EINVAL;
        !           916: 
        !           917:         } break;
        !           918: 
        !           919:         case DKIOCEJECT:                                         // eject(void);
        !           920:         {
        !           921:             //
        !           922:             // This ioctl asks that the media object be ejected from the drive.
        !           923:             //
        !           924: 
        !           925:             IODrive *    drive;
        !           926:             MinorTable * minors;
        !           927: 
        !           928:             drive  = OSDynamicCast(IODrive, minor->media->getProvider());
        !           929:             minors = gIOMediaBSDClient->getMinors();
        !           930: 
        !           931:             // Determine whether this media has a drive object as its parent.
        !           932: 
        !           933:             if ( drive == 0 )  { error = ENOTTY; break; }
        !           934: 
        !           935:             // Disable access to tables, matching, opens, closes, terminations.
        !           936: 
        !           937:             gIOMediaBSDClient->lockForArbitration();
        !           938: 
        !           939:             // Determine whether there are other opens on the device nodes that
        !           940:             // are associated with this anchor -- the one valid open is the one
        !           941:             // that issued this eject.
        !           942: 
        !           943:             if ( minors->getOpenCountForAnchorID(minor->anchorID) > 1 )
        !           944:             {
        !           945:                 error = EBUSY;
        !           946: 
        !           947:                 // Enable access to tables, matching, opens, closes, and so on.
        !           948: 
        !           949:                 gIOMediaBSDClient->unlockForArbitration();
        !           950:             }
        !           951:             else
        !           952:             {
        !           953:                 // Mark this minor as being in ejection flux (which means are in
        !           954:                 // a state where the media object has been closed but the device
        !           955:                 // node is still open; we must reject all future accesses to the
        !           956:                 // device node until it is closed;  note that we do this both on
        !           957:                 // success and failure of the ejection call).
        !           958: 
        !           959:                 minor->isEjecting = true;
        !           960: 
        !           961:                 // Enable access to tables, matching, opens, closes, and so on.
        !           962: 
        !           963:                 gIOMediaBSDClient->unlockForArbitration();
        !           964: 
        !           965:                 // Close the media object, since IODrive semantics require that
        !           966:                 // no opens exist when the ejection request in made.
        !           967: 
        !           968:                 minor->media->close(gIOMediaBSDClient);
        !           969: 
        !           970:                 // Eject the media.
        !           971: 
        !           972:                 error = gIOMediaBSDClient->errnoFromReturn(drive->ejectMedia());
        !           973:             }
        !           974: 
        !           975:         } break;
        !           976: 
        !           977:         default:
        !           978:         {
        !           979:             //
        !           980:             // A foreign ioctl was received.  Log an error to the console.
        !           981:             //
        !           982: 
        !           983:             IOLog( "%s: ioctl(%s\'%c\',%d,%d) is unsupported.\n",
        !           984:                    minor->name,
        !           985:                    ((cmd & IOC_INOUT) == IOC_INOUT) ? ("_IOWR,") :
        !           986:                      ( ((cmd & IOC_OUT) == IOC_OUT) ? ("_IOR,") :
        !           987:                        ( ((cmd & IOC_IN) == IOC_IN) ? ("_IOW,") :
        !           988:                          ( ((cmd & IOC_VOID) == IOC_VOID) ? ("_IO,") : "" ) ) ),
        !           989:                    (char) IOCGROUP(cmd),
        !           990:                    (int)  (cmd & 0xff),
        !           991:                    (int)  IOCPARM_LEN(cmd) );
        !           992: 
        !           993:             error = ENOTTY;
        !           994: 
        !           995:         } break;
        !           996:     }
        !           997: 
        !           998:     return error;                                       // (return error status)
        !           999: }
        !          1000: 
        !          1001: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1002: 
        !          1003: int dkioctl_bdev(dev_t dev, u_long cmd, caddr_t data, int f, struct proc * proc)
        !          1004: {
        !          1005:     //
        !          1006:     // dkioctl_bdev performs operations other than a read or write, specific to
        !          1007:     // the block device.
        !          1008:     //
        !          1009: 
        !          1010:     int         error = 0;
        !          1011:     MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(dev));
        !          1012: 
        !          1013:     if ( minor->isEjecting )  return EBADF;               // (is minor in flux?)
        !          1014: 
        !          1015:     //
        !          1016:     // Process the ioctl.
        !          1017:     //
        !          1018: 
        !          1019:     switch ( cmd )
        !          1020:     {
        !          1021:         case DKIOCGETBLOCKSIZE:                      // getBlockSize(int * out);
        !          1022:         {
        !          1023:             //
        !          1024:             // This ioctl returns the preferred (or overrided) block size of the
        !          1025:             // media object.
        !          1026:             //
        !          1027: 
        !          1028:             *(int *)data = (int) minor->bdevBlockSize;
        !          1029: 
        !          1030:         } break;
        !          1031: 
        !          1032:         case DKIOCSETBLOCKSIZE:                       // setBlockSize(int * in);
        !          1033:         {
        !          1034:             //
        !          1035:             // This ioctl overrides the block size for the media object, for the
        !          1036:             // duration of all block device opens at this minor.
        !          1037:             //
        !          1038: 
        !          1039:             if ( *(int *)data > 0 )
        !          1040:                 minor->bdevBlockSize = (UInt64) (*(int *)data);
        !          1041:             else
        !          1042:                 error = EINVAL;
        !          1043: 
        !          1044:         } break;
        !          1045: 
        !          1046:         case DKIOCGETBLOCKCOUNT:                    // getBlockCount(int * out);
        !          1047:         {
        !          1048:             //
        !          1049:             // This ioctl returns the size of the media object in blocks.  The
        !          1050:             // implied block size is returned by DKIOCGETBLOCKSIZE.
        !          1051:             //
        !          1052: 
        !          1053:             if ( minor->bdevBlockSize )
        !          1054:                 *(int *)data = (int) ( minor->media->getSize() /
        !          1055:                                        minor->bdevBlockSize    );
        !          1056:             else
        !          1057:                 *(int *)data = 0;
        !          1058: 
        !          1059:         } break;
        !          1060: 
        !          1061:         default:
        !          1062:         {
        !          1063:             //
        !          1064:             // Call the common ioctl handler for all other ioctls.
        !          1065:             //
        !          1066: 
        !          1067:             error = dkioctl(dev, cmd, data, f, proc);
        !          1068: 
        !          1069:         } break;
        !          1070:     }
        !          1071: 
        !          1072:     return error;                                       // (return error status)
        !          1073: }
        !          1074: 
        !          1075: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1076: 
        !          1077: int dksize(dev_t dev)
        !          1078: {
        !          1079:     //
        !          1080:     // dksize returns the block size of the media.
        !          1081:     //
        !          1082:     // This is a departure from BSD 4.4's definition of this function, that is,
        !          1083:     // it will not return the size of the disk partition, as would be expected
        !          1084:     // in a BSD 4.4 implementation.
        !          1085:     //
        !          1086: 
        !          1087:     MinorSlot * minor = gIOMediaBSDClient->getMinor(minor(dev));
        !          1088: 
        !          1089:     if ( minor->isEjecting )  return 0;                   // (is minor in flux?)
        !          1090: 
        !          1091:     return (int) minor->bdevBlockSize;                    // (return block size)
        !          1092: }
        !          1093: 
        !          1094: // =============================================================================
        !          1095: // Support For BSD Functions
        !          1096: 
        !          1097: inline dev_t DKR_GET_DEV(dkr_t dkr, dkrtype_t dkrtype)
        !          1098: {
        !          1099:     return (dkrtype == DKRTYPE_BUF)
        !          1100:            ? ((struct buf *)dkr)->b_dev
        !          1101:            : ((struct dio *)dkr)->dev;
        !          1102: }
        !          1103: 
        !          1104: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1105: 
        !          1106: inline UInt64 DKR_GET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype)
        !          1107: {
        !          1108:     return (dkrtype == DKRTYPE_BUF)
        !          1109:            ? ((struct buf *)dkr)->b_bcount
        !          1110:            : ((struct dio *)dkr)->uio->uio_resid;
        !          1111: }
        !          1112: 
        !          1113: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1114: 
        !          1115: inline UInt64 DKR_GET_BYTE_START(dkr_t dkr, dkrtype_t dkrtype)
        !          1116: {
        !          1117:     if (dkrtype == DKRTYPE_BUF)
        !          1118:     {
        !          1119:         struct buf * bp    = (struct buf *)dkr;
        !          1120:         MinorSlot *  minor = gIOMediaBSDClient->getMinor(minor(bp->b_dev));
        !          1121: 
        !          1122:         return bp->b_blkno * minor->bdevBlockSize;
        !          1123:     }
        !          1124:     return ((struct dio *)dkr)->uio->uio_offset;
        !          1125: }
        !          1126: 
        !          1127: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1128: 
        !          1129: inline bool DKR_IS_READ(dkr_t dkr, dkrtype_t dkrtype)
        !          1130: {
        !          1131:     return (dkrtype == DKRTYPE_BUF)
        !          1132:            ? ((((struct buf *)dkr)->b_flags & B_READ) == B_READ)
        !          1133:            : ((((struct dio *)dkr)->uio->uio_rw) == UIO_READ);
        !          1134: }
        !          1135: 
        !          1136: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1137: 
        !          1138: inline bool DKR_IS_ASYNCHRONOUS(dkr_t dkr, dkrtype_t dkrtype)
        !          1139: {
        !          1140:     return (dkrtype == DKRTYPE_BUF)
        !          1141:            ? true
        !          1142:            : false;
        !          1143: }
        !          1144: 
        !          1145: 
        !          1146: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1147: 
        !          1148: inline bool DKR_IS_RAW(dkr_t dkr, dkrtype_t dkrtype)
        !          1149: {
        !          1150:     return (dkrtype == DKRTYPE_BUF)
        !          1151:            ? false
        !          1152:            : true;
        !          1153: }
        !          1154: 
        !          1155: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1156: 
        !          1157: inline void DKR_SET_BYTE_COUNT(dkr_t dkr, dkrtype_t dkrtype, UInt64 bcount)
        !          1158: {
        !          1159:     if (dkrtype == DKRTYPE_BUF)
        !          1160:         ((struct buf *)dkr)->b_resid = ((struct buf *)dkr)->b_bcount - bcount;
        !          1161:     else
        !          1162:         ((struct dio *)dkr)->uio->uio_resid -= bcount;
        !          1163: }
        !          1164: 
        !          1165: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1166: 
        !          1167: inline void DKR_RUN_COMPLETION(dkr_t dkr, dkrtype_t dkrtype, IOReturn status)
        !          1168: {
        !          1169:     if (dkrtype == DKRTYPE_BUF)
        !          1170:     {
        !          1171:         struct buf * bp = (struct buf *)dkr;
        !          1172: 
        !          1173:         bp->b_error  = gIOMediaBSDClient->errnoFromReturn(status);   // (error?)
        !          1174:         bp->b_flags |= (status != kIOReturnSuccess) ? B_ERROR : 0;   // (error?)
        !          1175:         biodone(bp);                                       // (complete request)
        !          1176:     }
        !          1177: }
        !          1178: 
        !          1179: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1180: 
        !          1181: inline IOMemoryDescriptor * DKR_GET_BUFFER(dkr_t dkr, dkrtype_t dkrtype)
        !          1182: {
        !          1183:     if (dkrtype == DKRTYPE_BUF)
        !          1184:     {
        !          1185:         struct buf * bp = (struct buf *)dkr;
        !          1186: 
        !          1187:         return IOMemoryDescriptor::withAddress(                // (single-range)
        !          1188:           (vm_address_t) bp->b_data,
        !          1189:           (vm_size_t)    bp->b_bcount,
        !          1190:           (bp->b_flags & B_READ) ? kIODirectionIn : kIODirectionOut,
        !          1191:           (bp->b_flags & B_PHYS) ? get_user_task() : get_kernel_task() );
        !          1192:     }
        !          1193:     else
        !          1194:     {
        !          1195:         struct uio * uio = ((struct dio *)dkr)->uio;
        !          1196: 
        !          1197:         assert( sizeof(IOVirtualRange         ) == sizeof(iovec          ) ); 
        !          1198:         assert( sizeof(IOVirtualRange::address) == sizeof(iovec::iov_base) );
        !          1199:         assert( sizeof(IOVirtualRange::length ) == sizeof(iovec::iov_len ) );
        !          1200: 
        !          1201:         return IOMemoryDescriptor::withRanges(               // (multiple-range)
        !          1202:         (IOVirtualRange *) uio->uio_iov,
        !          1203:         (UInt32)           uio->uio_iovcnt,
        !          1204:         (uio->uio_rw     == UIO_READ    ) ? kIODirectionIn  : kIODirectionOut,
        !          1205:         (uio->uio_segflg != UIO_SYSSPACE) ? get_user_task() : get_kernel_task(),
        !          1206:         true );
        !          1207:     }
        !          1208: }
        !          1209: 
        !          1210: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1211: 
        !          1212: int dkreadwrite(dkr_t dkr, dkrtype_t dkrtype)
        !          1213: {
        !          1214:     //
        !          1215:     // dkreadwrite performs a read or write operation.
        !          1216:     //
        !          1217: 
        !          1218:     IOMemoryDescriptor * buffer;
        !          1219:     register UInt64      byteCount;
        !          1220:     register UInt64      byteStart;
        !          1221:     UInt64               mediaSize;
        !          1222:     MinorSlot *          minor;
        !          1223:     IOReturn             status;
        !          1224: 
        !          1225:     minor = gIOMediaBSDClient->getMinor(minor(DKR_GET_DEV(dkr, dkrtype)));
        !          1226: 
        !          1227:     if ( minor->isEjecting )                              // (is minor in flux?)
        !          1228:     {
        !          1229:         status = kIOReturnNoMedia;
        !          1230:         goto dkreadwriteErr;
        !          1231:     }
        !          1232: 
        !          1233:     if ( minor->media->isFormatted() == false )       // (is media unformatted?)
        !          1234:     {
        !          1235:         status = kIOReturnUnformattedMedia;
        !          1236:         goto dkreadwriteErr;
        !          1237:     }
        !          1238: 
        !          1239:     byteCount = DKR_GET_BYTE_COUNT(dkr, dkrtype);            // (get byte count)
        !          1240:     byteStart = DKR_GET_BYTE_START(dkr, dkrtype);            // (get byte start)
        !          1241:     mediaSize = minor->media->getSize();                     // (get media size)
        !          1242: 
        !          1243:     //
        !          1244:     // We must reject non-block-aligned requests to conform to BSD semantics.
        !          1245:     //
        !          1246: 
        !          1247:     if ( DKR_IS_RAW(dkr, dkrtype) )
        !          1248:     {
        !          1249:         UInt64 mediaBlockSize = minor->media->getPreferredBlockSize();
        !          1250: 
        !          1251:         if ( (byteStart % mediaBlockSize) || (byteCount % mediaBlockSize) )
        !          1252:         {
        !          1253:             status = kIOReturnNotAligned;
        !          1254:             goto dkreadwriteErr;
        !          1255:         }
        !          1256:     }
        !          1257: 
        !          1258:     //
        !          1259:     // We must take in account that short reads and writes are not considered
        !          1260:     // errors under BSD semantics.  We're to transfer as many bytes as can be
        !          1261:     // read or written from the medium and return no error.  Also, reads that
        !          1262:     // start at (or perhaps past) the end-of-media are not considered errors,
        !          1263:     // even though no data is transferred, while writes at (or past) the end-
        !          1264:     // of-media do indeed return errors.  This differs from IOMedia semantics
        !          1265:     // which is to fail the entire request without transferring a single byte
        !          1266:     // should it include for something past the end-of-media.   We must adapt
        !          1267:     // the IOMedia semantics to look like BSD semantics here.
        !          1268:     // 
        !          1269: 
        !          1270:     if ( byteStart >= mediaSize )     // (is start at or past the end-of-media?)
        !          1271:     {
        !          1272:         status = DKR_IS_READ(dkr,dkrtype) ? kIOReturnSuccess : kIOReturnIOError;
        !          1273:         goto dkreadwriteErr;
        !          1274:     }
        !          1275: 
        !          1276:     //
        !          1277:     // Build a descriptor which describes the buffer involved in the transfer.
        !          1278:     //
        !          1279: 
        !          1280:     buffer = DKR_GET_BUFFER(dkr, dkrtype);
        !          1281: 
        !          1282:     if ( buffer == 0 )                                           // (no buffer?)
        !          1283:     {
        !          1284:         status = kIOReturnNoMemory;
        !          1285:         goto dkreadwriteErr;
        !          1286:     }
        !          1287: 
        !          1288:     //
        !          1289:     // Clip the transfer buffer should this be a short read or write request.
        !          1290:     //
        !          1291: 
        !          1292:     if ( byteCount > mediaSize - byteStart )           // (clip at end-of-media)
        !          1293:     {
        !          1294:         IOMemoryDescriptor * originalBuffer = buffer;
        !          1295: 
        !          1296:         buffer = IOMemoryDescriptor::withSubRange(
        !          1297:                            /* descriptor    */ originalBuffer,
        !          1298:                            /* withOffset    */ 0,
        !          1299:                            /* withLength    */ mediaSize - byteStart,
        !          1300:                            /* withDirection */ originalBuffer->getDirection() );
        !          1301: 
        !          1302:         originalBuffer->release();   // (either retained above or about to fail)
        !          1303: 
        !          1304:         if ( buffer == 0 )                                      // (no buffer?)
        !          1305:         {
        !          1306:             status = kIOReturnNoMemory;
        !          1307:             goto dkreadwriteErr;
        !          1308:         }
        !          1309:     }
        !          1310: 
        !          1311:     //
        !          1312:     // Execute the transfer. 
        !          1313:     //
        !          1314: 
        !          1315:     if ( DKR_IS_ASYNCHRONOUS(dkr, dkrtype) )       // (an asynchronous request?)
        !          1316:     {
        !          1317:         IOStorageCompletion completion;
        !          1318: 
        !          1319:         completion.target    = dkr;
        !          1320:         completion.action    = dkreadwritecompletion;
        !          1321:         completion.parameter = (void *) dkrtype;
        !          1322: 
        !          1323:         if ( DKR_IS_READ(dkr, dkrtype) )                            // (a read?)
        !          1324:         {
        !          1325:             minor->media->read(  /* client     */ gIOMediaBSDClient,
        !          1326:                                  /* byteStart  */ byteStart,
        !          1327:                                  /* buffer     */ buffer,
        !          1328:                                  /* completion */ completion );          // (go)
        !          1329:         }
        !          1330:         else                                                       // (a write?)
        !          1331:         {
        !          1332:             minor->media->write( /* client     */ gIOMediaBSDClient,
        !          1333:                                  /* byteStart  */ byteStart,
        !          1334:                                  /* buffer     */ buffer,
        !          1335:                                  /* completion */ completion );          // (go)
        !          1336:         }
        !          1337:         status = kIOReturnSuccess;
        !          1338:     }
        !          1339:     else                                     // (is this a synchronous request?)
        !          1340:     {
        !          1341:         if ( DKR_IS_READ(dkr, dkrtype) )                            // (a read?)
        !          1342:         {
        !          1343:             status = minor->media->read(
        !          1344:                                  /* client          */ gIOMediaBSDClient,
        !          1345:                                  /* byteStart       */ byteStart,
        !          1346:                                  /* buffer          */ buffer,
        !          1347:                                  /* actualByteCount */ &byteCount );     // (go)
        !          1348:         }
        !          1349:         else                                                       // (a write?)
        !          1350:         {
        !          1351:             status = minor->media->write(
        !          1352:                                  /* client          */ gIOMediaBSDClient,
        !          1353:                                  /* byteStart       */ byteStart,
        !          1354:                                  /* buffer          */ buffer,
        !          1355:                                  /* actualByteCount */ &byteCount );     // (go)
        !          1356:         }
        !          1357: 
        !          1358:         dkreadwritecompletion(dkr, (void *)dkrtype, status, byteCount);
        !          1359:     }
        !          1360: 
        !          1361:     //
        !          1362:     // We release our retain on the buffer now, even though in the asynchronous
        !          1363:     // case, the object needs to exist for the duration of the transfer.  While
        !          1364:     // this might appear to be a mistake, it is not.   The layers below us will
        !          1365:     // have retained the buffer themselves.  On the last release, the buffer is
        !          1366:     // complete()'d appropriately.
        !          1367:     //
        !          1368: 
        !          1369:     buffer->release();                     // (release our retain on the buffer)
        !          1370: 
        !          1371:     return gIOMediaBSDClient->errnoFromReturn(status);  // (return error status)
        !          1372: 
        !          1373: dkreadwriteErr:
        !          1374: 
        !          1375:     dkreadwritecompletion(dkr, (void *)dkrtype, status, 0);
        !          1376: 
        !          1377:     return gIOMediaBSDClient->errnoFromReturn(status);  // (return error status)
        !          1378: }
        !          1379: 
        !          1380: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1381: 
        !          1382: void dkreadwritecompletion( void *   target,
        !          1383:                             void *   parameter,
        !          1384:                             IOReturn status,
        !          1385:                             UInt64   actualByteCount )
        !          1386: {
        !          1387:     //
        !          1388:     // dkreadwritecompletion cleans up after a read or write operation.
        !          1389:     //
        !          1390: 
        !          1391:     dkr_t     dkr      = (dkr_t) target;
        !          1392:     dkrtype_t dkrtype  = (dkrtype_t) (int) parameter;
        !          1393:     dev_t     dev      = DKR_GET_DEV(dkr, dkrtype);
        !          1394: 
        !          1395: #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
        !          1396:     UInt32    anchorID = gIOMediaBSDClient->getMinor(minor(dev))->anchorID;
        !          1397: 
        !          1398:     if ( anchorID < DK_NDRIVE )
        !          1399:     {
        !          1400:         IODrive * drive = dk_drive[anchorID];
        !          1401: 
        !          1402:         if ( drive )
        !          1403:         {
        !          1404:             dk_xfer[anchorID] = (long)
        !          1405:               ( drive->getStatistic(IODrive::kStatisticsReads ) +
        !          1406:                 drive->getStatistic(IODrive::kStatisticsWrites) );
        !          1407:             dk_wds[anchorID] = (long) ( 8 *
        !          1408:               ( drive->getStatistic(IODrive::kStatisticsBytesRead   ) +
        !          1409:                 drive->getStatistic(IODrive::kStatisticsBytesWritten) ) );
        !          1410:         }
        !          1411:     }
        !          1412: #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
        !          1413: 
        !          1414:     if ( status != kIOReturnSuccess )             // (log errors to the console)
        !          1415:     {
        !          1416:         IOLog( "%s: %s.\n",
        !          1417:                gIOMediaBSDClient->getMinor(minor(dev))->name,
        !          1418:                gIOMediaBSDClient->stringFromReturn(status) );
        !          1419:     }
        !          1420: 
        !          1421:     DKR_SET_BYTE_COUNT(dkr, dkrtype, actualByteCount);       // (set byte count)
        !          1422:     DKR_RUN_COMPLETION(dkr, dkrtype, status);                // (run completion)
        !          1423: }
        !          1424: 
        !          1425: // =============================================================================
        !          1426: // AnchorTable Class
        !          1427: 
        !          1428: AnchorTable::AnchorTable(UInt32 growCount, UInt32 maxCount)
        !          1429: {
        !          1430:     //
        !          1431:     // Initialize this object's minimal state.
        !          1432:     //
        !          1433: 
        !          1434:     _table          = 0;
        !          1435:     _tableCount     = 0;
        !          1436:     _tableGrowCount = growCount;
        !          1437:     _tableMaxCount  = maxCount;
        !          1438: }
        !          1439: 
        !          1440: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1441: 
        !          1442: AnchorTable::~AnchorTable()
        !          1443: {
        !          1444:     //
        !          1445:     // Free all of this object's outstanding resources.
        !          1446:     //
        !          1447: 
        !          1448:     for ( UInt32 anchorID = 0; anchorID < _tableCount; anchorID++ )
        !          1449:         if ( _table[anchorID].isAssigned )  remove(anchorID);
        !          1450: 
        !          1451:     if ( _table )  IODelete(_table, AnchorSlot, _tableCount);
        !          1452: }
        !          1453: 
        !          1454: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1455: 
        !          1456: UInt32 AnchorTable::insert(IOService * anchor)
        !          1457: {
        !          1458:     //
        !          1459:     // This method inserts the specified anchor into an unassigned slot in the
        !          1460:     // anchor table and returns its ID (or kInvalidAnchorID on a failure).
        !          1461:     //
        !          1462:     // Note that the anchor is transparently removed from the table should the
        !          1463:     // anchor terminate (or it is at least marked obsolete,  should references
        !          1464:     // to the anchor still exist in the minor table).
        !          1465:     //
        !          1466: 
        !          1467:     UInt32       anchorID;
        !          1468:     IONotifier * notifier;
        !          1469: 
        !          1470:     // Search for an unassigned slot in the anchor table.
        !          1471: 
        !          1472:     for ( anchorID = 0; anchorID < _tableCount; anchorID++ )
        !          1473:         if ( _table[anchorID].isAssigned == false )  break;
        !          1474: 
        !          1475:     // Was an unassigned slot found?  If not, grow the table.
        !          1476: 
        !          1477:     if ( anchorID == _tableCount )
        !          1478:     {
        !          1479:         AnchorSlot * newTable;
        !          1480:         UInt32       newTableCount;
        !          1481: 
        !          1482:         // We must expand the anchor table since no more slots are available.
        !          1483: 
        !          1484:         if ( _tableCount >= _tableMaxCount )  return kInvalidAnchorID;
        !          1485: 
        !          1486:         newTableCount = min(_tableGrowCount + _tableCount, _tableMaxCount);
        !          1487:         newTable      = IONew(AnchorSlot, newTableCount);
        !          1488: 
        !          1489:         if ( newTable == 0 )  return kInvalidAnchorID;
        !          1490: 
        !          1491:         bzero(newTable, newTableCount * sizeof(AnchorSlot));
        !          1492: 
        !          1493:         // Copy over the old table's entries, then free the old table.
        !          1494: 
        !          1495:         if ( _table )
        !          1496:         {
        !          1497:             bcopy(_table, newTable, _tableCount * sizeof(AnchorSlot));
        !          1498:             IODelete(_table, AnchorSlot, _tableCount);
        !          1499:         }
        !          1500:     
        !          1501:         // Obtain the next unassigned index (simple since we know the size of
        !          1502:         // the old table),  then update our instance variables to reflect the
        !          1503:         // new tables.
        !          1504: 
        !          1505:         anchorID    = _tableCount;
        !          1506:         _table      = newTable;
        !          1507:         _tableCount = newTableCount;
        !          1508:     }
        !          1509: 
        !          1510:     // Create a notification handler for the anchor's termination (post-stop);
        !          1511:     // the handler will remove the anchor transparently from the table if the
        !          1512:     // anchor terminates (or at least marks it obsolete, if references to the
        !          1513:     // anchor still exist in the minor table).
        !          1514: 
        !          1515:     notifier = anchor->registerInterest(
        !          1516:                           /* type        */ gIOGeneralInterest,
        !          1517:                           /* action      */ anchorChange,
        !          1518:                           /* target      */ (void *) this,
        !          1519:                           /* parameter   */ 0 );
        !          1520:   
        !          1521:     if ( notifier == 0 )  return kInvalidAnchorID;
        !          1522: 
        !          1523:     // Zero the new slot, fill it in, and retain the anchor object.
        !          1524: 
        !          1525:     bzero(&_table[anchorID], sizeof(AnchorSlot)); // (zero slot)
        !          1526: 
        !          1527:     _table[anchorID].isAssigned = true;           // (fill in slot)
        !          1528:     _table[anchorID].isObsolete = false;
        !          1529:     _table[anchorID].anchor     = anchor;
        !          1530:     _table[anchorID].notifier   = notifier;
        !          1531: 
        !          1532:     _table[anchorID].anchor->retain();            // (retain anchor)
        !          1533: 
        !          1534: #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
        !          1535:     if ( anchorID < DK_NDRIVE )
        !          1536:     {
        !          1537:         dk_drive[anchorID] = OSDynamicCast(IODrive, anchor);
        !          1538:         if ( anchorID + 1 > (UInt32) dk_ndrive )  dk_ndrive = anchorID + 1;
        !          1539:     }
        !          1540: #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
        !          1541: 
        !          1542:     return anchorID;
        !          1543: }
        !          1544: 
        !          1545: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1546: 
        !          1547: void AnchorTable::remove(UInt32 anchorID)
        !          1548: {
        !          1549:     //
        !          1550:     // This method removes the specified anchor from the anchor table.
        !          1551:     //
        !          1552: 
        !          1553:     assert(anchorID < _tableCount);
        !          1554:     assert(_table[anchorID].isAssigned);
        !          1555: 
        !          1556:     // Release the resources retained in the anchor slot and zero it.
        !          1557: 
        !          1558:     _table[anchorID].notifier->remove();
        !          1559:     _table[anchorID].anchor->release();           // (release anchor)
        !          1560: 
        !          1561:     bzero(&_table[anchorID], sizeof(AnchorSlot)); // (zero slot)
        !          1562: 
        !          1563: #ifdef IOMEDIABSDCLIENT_IOSTAT_SUPPORT
        !          1564:     if ( anchorID < DK_NDRIVE )
        !          1565:     {
        !          1566:         dk_drive[anchorID] = 0;
        !          1567:         for (dk_ndrive = DK_NDRIVE; dk_ndrive; dk_ndrive--)
        !          1568:         {
        !          1569:            if ( dk_drive[dk_ndrive - 1] )  break;
        !          1570:         }
        !          1571:     }
        !          1572: #endif IOMEDIABSDCLIENT_IOSTAT_SUPPORT
        !          1573: }
        !          1574: 
        !          1575: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1576: 
        !          1577: void AnchorTable::obsolete(UInt32 anchorID)
        !          1578: {
        !          1579:     //
        !          1580:     // This method obsoletes the specified anchor, that is, the slot is marked
        !          1581:     // as obsolete and will be removed later via the minor table remove method
        !          1582:     // once it detects references to the anchor ID drop to 0.   Once obsoleted,
        !          1583:     // the anchor can be considered to be removed, since it will not appear in
        !          1584:     // locate searches, even though behind the scenes it still occupies a slot.
        !          1585:     //
        !          1586: 
        !          1587:     assert(anchorID < _tableCount);
        !          1588:     assert(_table[anchorID].isAssigned);
        !          1589: 
        !          1590:     // Mark the anchor as obsolete so that it can be removed from the table as
        !          1591:     // soon as all its references go away (minor table's responsibility).
        !          1592: 
        !          1593:     _table[anchorID].isObsolete = true;
        !          1594: }
        !          1595: 
        !          1596: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1597: 
        !          1598: UInt32 AnchorTable::locate(IOService * anchor)
        !          1599: {
        !          1600:     //
        !          1601:     // This method searches for the specified anchor in the anchor table and
        !          1602:     // returns its ID (or kInvalidAnchorID on a failure).   It ignores slots
        !          1603:     // marked as obsolete.
        !          1604:     //
        !          1605: 
        !          1606:     for (UInt32 anchorID = 0; anchorID < _tableCount; anchorID++)
        !          1607:     {
        !          1608:         if ( _table[anchorID].isAssigned != false  &&
        !          1609:              _table[anchorID].isObsolete == false  &&
        !          1610:              _table[anchorID].anchor     == anchor )  return anchorID;
        !          1611:     }
        !          1612: 
        !          1613:     return kInvalidAnchorID;
        !          1614: }
        !          1615: 
        !          1616: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1617: 
        !          1618: bool AnchorTable::isObsolete(UInt32 anchorID)
        !          1619: {
        !          1620:     //
        !          1621:     // Determine whether the specified anchor ID is marked as obsolete.
        !          1622:     //
        !          1623: 
        !          1624:     assert(anchorID < _tableCount);
        !          1625:     assert(_table[anchorID].isAssigned);
        !          1626: 
        !          1627:     return _table[anchorID].isObsolete ? true : false;
        !          1628: }
        !          1629: 
        !          1630: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1631: 
        !          1632: IOReturn AnchorTable::anchorChange(    void *      /* target */,
        !          1633:                                        void *      /* parameter */,
        !          1634:                                        UInt32      messageType,
        !          1635:                                        IOService * anchor,
        !          1636:                                        void *      /* messageArgument */,
        !          1637:                                        vm_size_t   /* argSize */ )
        !          1638: {
        !          1639:     if ( messageType != kIOMessageServiceIsTerminated )
        !          1640:         return kIOReturnSuccess;
        !          1641: 
        !          1642:     //
        !          1643:     // Notification handler for anchor termination (post-stop).
        !          1644:     //
        !          1645:     
        !          1646:     UInt32 anchorID;
        !          1647: 
        !          1648:     assert(gIOMediaBSDClient);
        !          1649: 
        !          1650:     // Disable access to tables, matching, opens, closes, and terminations.
        !          1651: 
        !          1652:     gIOMediaBSDClient->lockForArbitration();
        !          1653: 
        !          1654:     // Determine whether this anchor is in the anchor table (obsolete occurences
        !          1655:     // are skipped in the search, as appropriate, since those anchor IDs will be
        !          1656:     // removed as it is).
        !          1657: 
        !          1658:     anchorID = gIOMediaBSDClient->getAnchors()->locate(anchor);
        !          1659: 
        !          1660:     if ( anchorID != kInvalidAnchorID )
        !          1661:     {
        !          1662:         // Determine whether this anchor is still has references in the minor
        !          1663:         // table.  If it does,  we mark the the anchor as obsolete so that it
        !          1664:         // will be removed later,  once references to it go to zero (which is
        !          1665:         // handled by MinorTable::remove).
        !          1666: 
        !          1667:         if ( gIOMediaBSDClient->getMinors()->hasReferencesToAnchorID(anchorID) )
        !          1668:             gIOMediaBSDClient->getAnchors()->obsolete(anchorID);
        !          1669:         else
        !          1670:             gIOMediaBSDClient->getAnchors()->remove(anchorID);
        !          1671:     }
        !          1672: 
        !          1673:     // Enable access to tables, matching, opens, closes, and terminations.
        !          1674: 
        !          1675:     gIOMediaBSDClient->unlockForArbitration();    
        !          1676: 
        !          1677:     return kIOReturnSuccess;
        !          1678: }
        !          1679: 
        !          1680: // =============================================================================
        !          1681: // MinorTable Class
        !          1682: 
        !          1683: MinorTable::MinorTable(UInt32 growCount, UInt32 maxCount)
        !          1684: {
        !          1685:     //
        !          1686:     // Initialize this object's minimal state.
        !          1687:     //
        !          1688: 
        !          1689:     _table          = 0;
        !          1690:     _tableCount     = 0;
        !          1691:     _tableGrowCount = growCount;
        !          1692:     _tableMaxCount  = maxCount;
        !          1693: }
        !          1694: 
        !          1695: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1696: 
        !          1697: MinorTable::~MinorTable()
        !          1698: {
        !          1699:     //
        !          1700:     // Free all of this object's outstanding resources.
        !          1701:     //
        !          1702: 
        !          1703:     for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
        !          1704:         if ( _table[minorID].isAssigned )  remove(minorID);
        !          1705: 
        !          1706:     if ( _table )  IODelete(_table, MinorSlot, _tableCount);
        !          1707: }
        !          1708: 
        !          1709: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1710: 
        !          1711: UInt32 MinorTable::insert(IOMedia * media, UInt32 anchorID, char * slicePath)
        !          1712: {
        !          1713:     //
        !          1714:     // This method inserts the specified media/anchorID pair into an unassigned
        !          1715:     // slot in the minor table and returns its ID (or kInvalidMinorID on error).
        !          1716:     //
        !          1717:     // Note that the bdev and cdev nodes are published as a result of this call,
        !          1718:     // with the name "[r]disk<anchorID><slicePath>".  For instance, "disk2s3s1"
        !          1719:     // for an anchorID of 2 and slicePath of "s3s1".
        !          1720:     //
        !          1721: 
        !          1722:     void *       bdevNode;
        !          1723:     void *       cdevNode;
        !          1724:     UInt32       minorID;
        !          1725:     char *       minorName;
        !          1726:     UInt32       minorNameSize;
        !          1727: 
        !          1728:     // Search for an unassigned slot in the minor table.
        !          1729: 
        !          1730:     for ( minorID = 0; minorID < _tableCount; minorID++ )
        !          1731:         if ( _table[minorID].isAssigned == false )  break;
        !          1732: 
        !          1733:     // Was an unassigned slot found?  If not, grow the table.
        !          1734: 
        !          1735:     if ( minorID == _tableCount )
        !          1736:     {
        !          1737:         MinorSlot * newTable;
        !          1738:         UInt32      newTableCount;
        !          1739: 
        !          1740:         // We must expand the minor table since no more slots are available.
        !          1741: 
        !          1742:         if ( _tableCount >= _tableMaxCount)  return kInvalidMinorID;
        !          1743: 
        !          1744:         newTableCount = min(_tableGrowCount + _tableCount, _tableMaxCount);
        !          1745:         newTable      = IONew(MinorSlot, newTableCount);
        !          1746: 
        !          1747:         if ( newTable == 0 )  return kInvalidMinorID;
        !          1748: 
        !          1749:         bzero(newTable, newTableCount * sizeof(MinorSlot));
        !          1750: 
        !          1751:         // Copy over the old table's entries, then free the old table.
        !          1752: 
        !          1753:         if ( _table )
        !          1754:         {
        !          1755:             bcopy(_table, newTable, _tableCount * sizeof(MinorSlot));
        !          1756:             IODelete(_table, MinorSlot, _tableCount);
        !          1757:         }
        !          1758: 
        !          1759:         // Obtain the next unassigned index (simple since we know the size of
        !          1760:         // the old table),  then update our instance variables to reflect the
        !          1761:         // new tables.
        !          1762: 
        !          1763:         minorID     = _tableCount;
        !          1764:         _table      = newTable;
        !          1765:         _tableCount = newTableCount;
        !          1766:     }
        !          1767: 
        !          1768:     // Create a buffer large enough to hold the full name of the minor.
        !          1769: 
        !          1770:     minorNameSize = strlen("disk#");
        !          1771:     for (unsigned temp = anchorID; temp >= 10; temp /= 10)  minorNameSize++;
        !          1772:     minorNameSize += strlen(slicePath);
        !          1773:     minorNameSize += 1;
        !          1774:     minorName = IONew(char, minorNameSize);
        !          1775: 
        !          1776:     // Create a block and character device node in BSD for this media.
        !          1777: 
        !          1778:     bdevNode = devfs_make_node( /* dev        */ makedev(kMajor, minorID),
        !          1779:                                 /* type       */ DEVFS_BLOCK, 
        !          1780:                                 /* owner      */ UID_ROOT,
        !          1781:                                 /* group      */ GID_OPERATOR, 
        !          1782:                                 /* permission */ media->isWritable()?0640:0440, 
        !          1783:                                 /* name (fmt) */ "disk%d%s",
        !          1784:                                 /* name (arg) */ anchorID,
        !          1785:                                 /* name (arg) */ slicePath );
        !          1786: 
        !          1787:     cdevNode = devfs_make_node( /* dev        */ makedev(kMajor, minorID),
        !          1788:                                 /* type       */ DEVFS_CHAR, 
        !          1789:                                 /* owner      */ UID_ROOT,
        !          1790:                                 /* group      */ GID_OPERATOR, 
        !          1791:                                 /* permission */ media->isWritable()?0640:0440,
        !          1792:                                 /* name (fmt) */ "rdisk%d%s",
        !          1793:                                 /* name (arg) */ anchorID,
        !          1794:                                 /* name (arg) */ slicePath );
        !          1795: 
        !          1796:     if ( minorName == 0 || bdevNode == 0 || cdevNode == 0 )
        !          1797:     {
        !          1798:         if ( cdevNode )   devfs_remove(cdevNode);
        !          1799:         if ( bdevNode )   devfs_remove(bdevNode);
        !          1800:         if ( minorName )  IODelete(minorName, char, minorNameSize);
        !          1801: 
        !          1802:         return kInvalidMinorID;
        !          1803:     }
        !          1804: 
        !          1805:     // Construct a name for the node.
        !          1806: 
        !          1807:     sprintf(minorName, "disk%ld%s", anchorID, slicePath);
        !          1808:     assert(strlen(minorName) + 1 == minorNameSize);
        !          1809: 
        !          1810:     // Zero the new slot, fill it in, and retain the media object.
        !          1811: 
        !          1812:     bzero(&_table[minorID], sizeof(MinorSlot));    // (zero slot)
        !          1813: 
        !          1814:     _table[minorID].isAssigned    = true;          // (fill in slot)
        !          1815:     _table[minorID].isEjecting    = false;
        !          1816:     _table[minorID].isObsolete    = false;
        !          1817:     _table[minorID].anchorID      = anchorID;
        !          1818:     _table[minorID].media         = media;
        !          1819:     _table[minorID].name          = minorName;
        !          1820:     _table[minorID].bdevBlockSize = media->getPreferredBlockSize();
        !          1821:     _table[minorID].bdevNode      = bdevNode;
        !          1822:     _table[minorID].bdevOpen      = false;
        !          1823:     _table[minorID].cdevNode      = cdevNode;
        !          1824:     _table[minorID].cdevOpen      = false;
        !          1825: 
        !          1826:     _table[minorID].media->retain();               // (retain media)
        !          1827: 
        !          1828:     return minorID;
        !          1829: }
        !          1830: 
        !          1831: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1832: 
        !          1833: void MinorTable::remove(UInt32 minorID)
        !          1834: {
        !          1835:     //
        !          1836:     // This method removes the specified minor from the minor table.
        !          1837:     //
        !          1838: 
        !          1839:     UInt32 anchorID;
        !          1840: 
        !          1841:     assert(minorID < _tableCount);
        !          1842:     assert(_table[minorID].isAssigned);
        !          1843: 
        !          1844:     assert(_table[minorID].isEjecting == false);
        !          1845:     assert(_table[minorID].bdevOpen == false);
        !          1846:     assert(_table[minorID].cdevOpen == false);
        !          1847: 
        !          1848:     anchorID = _table[minorID].anchorID;
        !          1849: 
        !          1850:     // Release the resources retained in the minor slot and zero it.
        !          1851: 
        !          1852:     devfs_remove(_table[minorID].cdevNode);
        !          1853:     devfs_remove(_table[minorID].bdevNode);
        !          1854:     IODelete(_table[minorID].name, char, strlen(_table[minorID].name) + 1);
        !          1855:     _table[minorID].media->release();              // (release media)
        !          1856: 
        !          1857:     bzero(&_table[minorID], sizeof(MinorSlot));    // (zero slot)
        !          1858: 
        !          1859:     // Determine whether the associated anchor ID is marked as obsolete.  If it
        !          1860:     // is and there are no other references to the anchor ID in the minor table,
        !          1861:     // we remove the anchor ID from the anchor table.
        !          1862: 
        !          1863:     assert(gIOMediaBSDClient);
        !          1864: 
        !          1865:     if ( gIOMediaBSDClient->getAnchors()->isObsolete(anchorID) )
        !          1866:     {
        !          1867:         if ( hasReferencesToAnchorID(anchorID) == false )
        !          1868:             gIOMediaBSDClient->getAnchors()->remove(anchorID);
        !          1869:     }
        !          1870: }
        !          1871: 
        !          1872: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1873: 
        !          1874: UInt32 MinorTable::locate(IOMedia * media)
        !          1875: {
        !          1876:     //
        !          1877:     // This method searches for the specified media in the minor table and
        !          1878:     // returns its ID (or kInvalidMinorID on an error).   It ignores slots
        !          1879:     // marked as obsolete.
        !          1880:     //
        !          1881: 
        !          1882:     for (UInt32 minorID = 0; minorID < _tableCount; minorID++)
        !          1883:     {
        !          1884:         if ( _table[minorID].isAssigned != false &&
        !          1885:              _table[minorID].isObsolete == false &&
        !          1886:              _table[minorID].media      == media )  return minorID;
        !          1887:     }
        !          1888: 
        !          1889:     return kInvalidMinorID;
        !          1890: }
        !          1891: 
        !          1892: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1893: 
        !          1894: UInt32 MinorTable::getOpenCountForAnchorID(UInt32 anchorID)
        !          1895: {
        !          1896:     //
        !          1897:     // This method obtains a count of opens on the minors associated with the
        !          1898:     // specified anchor ID.  A block device open is counted separately from a
        !          1899:     // character device open.
        !          1900:     //
        !          1901: 
        !          1902:     UInt32 opens = 0;
        !          1903: 
        !          1904:     for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
        !          1905:     {
        !          1906:         if ( _table[minorID].isAssigned != false    &&
        !          1907:              _table[minorID].anchorID   == anchorID )
        !          1908:         {
        !          1909:             opens += (_table[minorID].bdevOpen) ? 1 : 0;
        !          1910:             opens += (_table[minorID].cdevOpen) ? 1 : 0;
        !          1911:         }
        !          1912:     }
        !          1913: 
        !          1914:     return opens;
        !          1915: }
        !          1916: 
        !          1917: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1918: 
        !          1919: IOMedia * MinorTable::getWholeMediaAtAnchorID(UInt32 anchorID)
        !          1920: {
        !          1921:     //
        !          1922:     // This method obtains the whole media associated with the specified anchor
        !          1923:     // ID.
        !          1924:     //
        !          1925: 
        !          1926:     for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
        !          1927:     {
        !          1928:         if ( _table[minorID].isAssigned != false    &&
        !          1929:              _table[minorID].anchorID   == anchorID &&
        !          1930:              _table[minorID].media->isWhole() )  return _table[minorID].media;
        !          1931:     }
        !          1932: 
        !          1933:     return 0;
        !          1934: }
        !          1935: 
        !          1936: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1937: 
        !          1938: bool MinorTable::hasReferencesToAnchorID(UInt32 anchorID)
        !          1939: {
        !          1940:     //
        !          1941:     // This method determines whether there are assigned minors in the minor
        !          1942:     // table that refer to the specified anchor ID.
        !          1943:     //
        !          1944: 
        !          1945:     for ( UInt32 minorID = 0; minorID < _tableCount; minorID++ )
        !          1946:     {
        !          1947:         if ( _table[minorID].isAssigned != false    &&
        !          1948:              _table[minorID].anchorID   == anchorID )  return true;
        !          1949:     }
        !          1950: 
        !          1951:     return false;
        !          1952: }
        !          1953: 
        !          1954: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1955: 
        !          1956: MinorSlot * MinorTable::getMinor(UInt32 minorID)
        !          1957: {
        !          1958:     //
        !          1959:     // Obtain the structure describing the specified minor.
        !          1960:     //
        !          1961: 
        !          1962:     if ( minorID < _tableCount && _table[minorID].isAssigned )
        !          1963:         return &_table[minorID];
        !          1964:     else
        !          1965:         return 0;
        !          1966: }
        !          1967: 
        !          1968: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1969: 
        !          1970: void MinorTable::obsolete(UInt32 minorID)
        !          1971: {
        !          1972:     //
        !          1973:     // This method obsoletes the specified minor, that is, the slot is marked
        !          1974:     // as obsolete and will be removed later via the dkclose function once it
        !          1975:     // detects the last close arrive.  Once obsoleted, the minor can be cons-
        !          1976:     // idered to be removed, since it will not appear in locate searches.
        !          1977:     //
        !          1978: 
        !          1979:     assert(minorID < _tableCount);
        !          1980:     assert(_table[minorID].isAssigned);
        !          1981: 
        !          1982:     // Mark the minor as obsolete so that it can be removed from the table as
        !          1983:     // soon as the last close arrives (dkclose function's responsibility).
        !          1984: 
        !          1985:     _table[minorID].isObsolete = true;
        !          1986: }
        !          1987: 
        !          1988: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        !          1989: 
        !          1990: bool MinorTable::isObsolete(UInt32 minorID)
        !          1991: {
        !          1992:     //
        !          1993:     // Determine whether the specified minor ID is marked as obsolete.
        !          1994:     //
        !          1995: 
        !          1996:     assert(minorID < _tableCount);
        !          1997:     assert(_table[minorID].isAssigned);
        !          1998: 
        !          1999:     return _table[minorID].isObsolete ? true : false;
        !          2000: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.