1/*
2 * Copyright (c) 1998-2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <IOKit/assert.h>
25#include <IOKit/IOBufferMemoryDescriptor.h>
26#include <IOKit/IOLib.h>
27#include <IOKit/storage/IOCDPartitionScheme.h>
28
29#define super IOPartitionScheme
30OSDefineMetaClassAndStructors(IOCDPartitionScheme, IOPartitionScheme);
31
32#define kIOCDPartitionSchemeContentTable "Content Table"
33
34IOCDMedia * IOCDPartitionScheme::getProvider() const
35{
36    //
37    // Obtain this object's provider.  We override the superclass's method
38    // to return a more specific subclass of OSObject -- IOCDMedia.   This
39    // method serves simply as a convenience to subclass developers.
40    //
41
42    return (IOCDMedia *) IOService::getProvider();
43}
44
45bool IOCDPartitionScheme::init(OSDictionary * properties)
46{
47    //
48    // Initialize this object's minimal state.
49    //
50
51    // State our assumptions.
52
53    assert(sizeof(CDTOC)           ==  4);          // (compiler/platform check)
54    assert(sizeof(CDTOCDescriptor) == 11);          // (compiler/platform check)
55
56    // Ask our superclass' opinion.
57
58    if (super::init(properties) == false)  return false;
59
60    // Initialize our state.
61
62    _partitions = 0;
63
64    return true;
65}
66
67void IOCDPartitionScheme::free()
68{
69    //
70    // Free all of this object's outstanding resources.
71    //
72
73    if ( _partitions )  _partitions->release();
74
75    super::free();
76}
77
78IOService * IOCDPartitionScheme::probe(IOService * provider, SInt32 * score)
79{
80    //
81    // Determine whether the provider media contains CD partitions.
82    //
83
84    // State our assumptions.
85
86    assert(OSDynamicCast(IOCDMedia, provider));
87
88    // Ask superclass' opinion.
89
90    if (super::probe(provider, score) == 0)  return 0;
91
92    // Scan the provider media for CD partitions.
93
94    _partitions = scan(score);
95
96    return ( _partitions ) ? this : 0;
97}
98
99bool IOCDPartitionScheme::start(IOService * provider)
100{
101    //
102    // Publish the new media objects which represent our partitions.
103    //
104
105    IOMedia *    partition;
106    OSIterator * partitionIterator;
107
108    // State our assumptions.
109
110    assert(_partitions);
111
112    // Ask our superclass' opinion.
113
114    if ( super::start(provider) == false )  return false;
115
116    // Attach and register the new media objects representing our partitions.
117
118    partitionIterator = OSCollectionIterator::withCollection(_partitions);
119    if ( partitionIterator == 0 )  return false;
120
121    while ( (partition = (IOMedia *) partitionIterator->getNextObject()) )
122    {
123        if ( partition->attach(this) )
124        {
125            partition->registerService();
126        }
127    }
128
129    partitionIterator->release();
130
131    return true;
132}
133
134OSSet * IOCDPartitionScheme::scan(SInt32 * score)
135{
136    //
137    // Scan the provider media for CD partitions (in TOC).  Returns the set
138    // of media objects representing each of the partitions (the retain for
139    // the set is passed to the caller), or null should no CD partitions be
140    // found.  The default probe score can be adjusted up or down, based on
141    // the confidence of the scan.
142    //
143
144    struct CDSession
145    {
146        UInt32 formed:1;
147        UInt32 leadOut;
148    };
149
150    struct CDTrack
151    {
152        UInt32            block;
153        CDSectorSize      blockSize;
154        CDSectorType      blockType;
155        CDTOCDescriptor * descriptor;
156        UInt32            session:8;
157    };
158
159    #define kCDSessionMaxIndex 0x63
160
161    #define kCDTrackMinIndex   0x01
162    #define kCDTrackMaxIndex   0x63
163
164    IOBufferMemoryDescriptor * buffer          = 0;
165    IOCDMedia *                media           = getProvider();
166    UInt64                     mediaBlockSize  = media->getPreferredBlockSize();
167    bool                       mediaIsOpen     = false;
168    OSSet *                    partitions      = 0;
169    CDSession *                sessions        = 0;
170    UInt32                     sessionMinIndex = kCDSessionMaxIndex + 1;
171    UInt32                     sessionMaxIndex = 0;
172    CDTOC *                    toc             = 0;
173    UInt32                     tocCount        = 0;
174    CDTrack *                  tracks          = 0;
175    UInt32                     trackMinIndex   = kCDTrackMaxIndex + 1;
176    UInt32                     trackMaxIndex   = 0;
177    CDTrack *                  trackMaxLinked  = 0;
178
179    // State our assumptions.
180
181    assert(mediaBlockSize == kCDSectorSizeWhole);
182
183    // Determine whether this media is formatted.
184
185    if ( media->isFormatted() == false )  goto scanErr;
186
187    // Allocate a buffer large enough to hold a whole 2352-byte sector.
188
189    buffer = IOBufferMemoryDescriptor::withCapacity(
190                                           /* capacity      */ mediaBlockSize,
191                                           /* withDirection */ kIODirectionIn );
192    if ( buffer == 0 )  goto scanErr;
193
194    // Allocate a set to hold the set of media objects representing partitions.
195
196    partitions = OSSet::withCapacity(2);
197    if ( partitions == 0 )  goto scanErr;
198
199    // Open the media with read access.
200
201    mediaIsOpen = media->open(this, 0, kIOStorageAccessReader);
202    if ( mediaIsOpen == false )  goto scanErr;
203
204    // Obtain the table of contents.
205
206    toc = media->getTOC();
207    if ( toc )  tocCount = CDTOCGetDescriptorCount(toc);
208
209    // Allocate a list large enough to hold information about each session.
210
211    sessions = IONew(CDSession, kCDSessionMaxIndex + 1);
212    if ( sessions == 0 )  goto scanErr;
213
214    bzero(sessions, (kCDSessionMaxIndex + 1) * sizeof(CDSession));
215
216    // Allocate a list large enough to hold information about each track.
217
218    tracks = IONew(CDTrack, kCDTrackMaxIndex + 1);
219    if ( tracks == 0 )  goto scanErr;
220
221    bzero(tracks, (kCDTrackMaxIndex + 1) * sizeof(CDTrack));
222
223    // Scan the table of contents, gathering information about the sessions
224    // and tracks on the CD, but without making assumptions about the order
225    // of the entries in the table.
226
227    for ( unsigned index = 0; index < tocCount; index++ )
228    {
229        CDTOCDescriptor * descriptor = toc->descriptors + index;
230
231        // Determine whether this is an audio or data track descriptor.
232
233        if ( descriptor->point   >= kCDTrackMinIndex   &&
234             descriptor->point   <= kCDTrackMaxIndex   &&
235             descriptor->adr     == 0x01               &&
236             descriptor->session <= kCDSessionMaxIndex )
237        {
238            CDTrack * track = tracks + descriptor->point;
239
240            // Record the relevant information about this track.
241
242            track->block      = CDConvertMSFToClippedLBA(descriptor->p);
243            track->descriptor = descriptor;
244            track->session    = descriptor->session;
245
246            if ( (descriptor->control & 0x04) )                 // (data track?)
247            {
248                track->blockSize = kCDSectorSizeMode1;
249                track->blockType = kCDSectorTypeMode1;
250            }
251            else                                               // (audio track?)
252            {
253                track->blockSize = kCDSectorSizeCDDA;
254                track->blockType = kCDSectorTypeCDDA;
255            }
256
257            trackMinIndex = min(descriptor->point, trackMinIndex);
258            trackMaxIndex = max(descriptor->point, trackMaxIndex);
259        }
260
261        // Determine whether this is a lead-in (A0) descriptor.
262
263        else if ( descriptor->point   == 0xA0 &&
264                  descriptor->adr     == 0x01 &&
265                  descriptor->session <= kCDSessionMaxIndex )
266        {
267            CDSession * session = sessions + descriptor->session;
268
269            // Record whether the session has "form 1" or "form 2" tracks.
270
271            session->formed = ( descriptor->p.second ) ? true : false;
272        }
273
274        // Determine whether this is a lead-out (A2) descriptor.
275
276        else if ( descriptor->point   == 0xA2               &&
277                  descriptor->adr     == 0x01               &&
278                  descriptor->session <= kCDSessionMaxIndex )
279        {
280            CDSession * session = sessions + descriptor->session;
281
282            // Record the position of the session lead-out.
283
284            session->leadOut = CDConvertMSFToClippedLBA(descriptor->p);
285
286            sessionMinIndex = min(descriptor->session, sessionMinIndex);
287            sessionMaxIndex = max(descriptor->session, sessionMaxIndex);
288        }
289    }
290
291    // Pre-scan the ordered list of tracks.
292
293    for ( unsigned index = trackMinIndex; index <= trackMaxIndex; index++ )
294    {
295        CDTrack * track = tracks + index;
296
297        // Validate the existence of this track (and its session).
298
299        if ( track->descriptor == 0 || sessions[track->session].leadOut == 0 )
300        {
301            goto scanErr;
302        }
303
304        // Determine the block type, and linkage requirement, for this track.
305
306        if ( track->blockType == kCDSectorTypeMode1 )           // (data track?)
307        {
308            IOReturn status;
309
310            // Read a whole sector from the data track into our buffer.
311
312            status = media->read( /* client    */ this,
313                                  /* byteStart */ track->block * mediaBlockSize,
314                                  /* buffer    */ buffer );
315
316            if ( status == kIOReturnSuccess )
317            {
318                UInt8 * sector = (UInt8 *) buffer->getBytesNoCopy();
319
320                // Determine whether this is a "mode 2" data track.
321
322                if ( sector[15] == 0x02 )
323                {
324                    // Determine whether this is a "mode 2 formless",
325                    // "mode 2 form 1" or "mode 2 form 2" data track.
326
327                    if ( sessions[track->session ].formed ||
328                         sessions[sessionMinIndex].formed )
329                    {
330                        if ( (sector[18] & 0x20) )
331                        {
332                            track->blockSize = kCDSectorSizeMode2Form2;
333                            track->blockType = kCDSectorTypeMode2Form2;
334
335                            trackMaxLinked = track;
336                        }
337                        else
338                        {
339                            track->blockSize = kCDSectorSizeMode2Form1;
340                            track->blockType = kCDSectorTypeMode2Form1;
341
342                            // Determine whether this is a linked data track.
343
344                            if ( memcmp(sector + 24, "ER", 2) )
345                            {
346                                trackMaxLinked = track;
347                            }
348                        }
349                    }
350                    else
351                    {
352                        track->blockSize = kCDSectorSizeMode2;
353                        track->blockType = kCDSectorTypeMode2;
354
355                        trackMaxLinked = track;
356                    }
357                }
358
359                // Determine whether this is a linked "mode 1" data track.
360
361                else if ( memcmp(sector + 16, "ER", 2) )
362                {
363                    trackMaxLinked = track;
364                }
365            }
366            else
367            {
368                trackMaxLinked = track;
369            }
370        }
371    }
372
373    // Create a media object to represent the linked data tracks, the hidden
374    // pre-gap-area data track, or even both, if it is applicable to this CD.
375
376    if ( trackMinIndex > kCDTrackMaxIndex ||                     // (no tracks?)
377         trackMaxLinked                   ||                 // (linked tracks?)
378         tracks[trackMinIndex].block      )                  // (hidden tracks?)
379    {
380        CDTOCDescriptor descriptor;
381        UInt32          trackBlockNext;
382        CDSectorSize    trackBlockSize;
383        CDSectorType    trackBlockType;
384        UInt64          trackSize;
385
386        descriptor.session        = 0x01;
387        descriptor.control        = 0x04;
388        descriptor.adr            = 0x01;
389        descriptor.tno            = 0x00;
390        descriptor.point          = 0x00;
391        descriptor.address.minute = 0x00;
392        descriptor.address.second = 0x00;
393        descriptor.address.frame  = 0x00;
394        descriptor.zero           = 0x00;
395        descriptor.p              = CDConvertLBAToMSF(0);
396
397        if ( trackMinIndex > kCDTrackMaxIndex )                  // (no tracks?)
398        {
399            if ( sessionMinIndex > kCDSessionMaxIndex )        // (no sessions?)
400            {
401                if ( media->isWritable() == false )  goto scanErr;
402            }
403            else
404            {
405                descriptor.session = sessionMaxIndex;
406            }
407
408            if ( media->isWritable() )                   // (is still writable?)
409            {
410                CDPMA * pma      = (CDPMA *) buffer->getBytesNoCopy();
411                UInt32  pmaCount = 0;
412                UInt16  pmaSize  = 0;
413
414                trackBlockNext = media->getSize() / mediaBlockSize;
415                trackBlockSize = kCDSectorSizeMode2Form1;
416                trackBlockType = kCDSectorTypeMode2Form1;
417
418                // Determine whether this is a "mode 1" data track.
419
420                media->readTOC( /* buffer               */ buffer,
421                                /* format               */ kCDTOCFormatPMA,
422                                /* formatAsTime         */ true,
423                                /* trackOrSessionNumber */ 0,
424                                /* actualByteCount      */ &pmaSize );
425
426                pmaSize  = ( pmaSize <= sizeof(CDPMA) )
427                               ? pmaSize
428                               : min( pmaSize,
429                                      OSSwapBigToHostInt16(pma->dataLength) +
430                                                    sizeof(pma->dataLength) );
431                pmaCount = ( pmaSize <= sizeof(CDPMA) )
432                               ? 0
433                               : ( pmaSize - sizeof(CDPMA) ) /
434                                             sizeof(CDPMADescriptor);
435
436                for ( unsigned index = 0; index < pmaCount; index++ )
437                {
438                    if ( pma->descriptors[index].adr == 0x2 )
439                    {
440                        if ( pma->descriptors[index].p.second == 0x00 )
441                        {
442                            trackBlockSize = kCDSectorSizeMode1;
443                            trackBlockType = kCDSectorTypeMode1;
444                            break;
445                        }
446                    }
447                }
448            }
449            else
450            {
451                trackBlockNext = sessions[sessionMaxIndex].leadOut;
452                trackBlockSize = kCDSectorSizeMode2Form1;
453                trackBlockType = kCDSectorTypeMode2Form1;
454            }
455        }
456        else if ( trackMaxLinked )                           // (linked tracks?)
457        {
458            descriptor.session = sessionMaxIndex;
459            descriptor.control = trackMaxLinked->descriptor->control;
460
461            if ( media->isWritable() )                   // (is still writable?)
462            {
463                trackBlockNext = media->getSize() / mediaBlockSize;
464            }
465            else
466            {
467                trackBlockNext = sessions[sessionMaxIndex].leadOut;
468            }
469
470            if ( trackMaxLinked->blockType == kCDSectorTypeMode1 )
471            {
472                trackBlockSize = kCDSectorSizeMode1;
473                trackBlockType = kCDSectorTypeMode1;
474            }
475            else
476            {
477                trackBlockSize = kCDSectorSizeMode2Form1;
478                trackBlockType = kCDSectorTypeMode2Form1;
479            }
480        }
481        else                                                 // (hidden tracks?)
482        {
483            IOReturn status;
484
485            descriptor.session = sessionMinIndex;
486
487            trackBlockNext = tracks[trackMinIndex].block;
488            trackBlockSize = kCDSectorSizeMode1;
489            trackBlockType = kCDSectorTypeMode1;
490
491            // Read a whole sector from the hidden track into our buffer.
492
493            status = media->read( /* client    */ this,
494                                  /* byteStart */ 0,
495                                  /* buffer    */ buffer );
496
497            if ( status == kIOReturnSuccess )
498            {
499                UInt8 * sector = (UInt8 *) buffer->getBytesNoCopy();
500
501                // Determine whether this is an audio track.
502
503                if ( sector[ 0] != 0x00 ||
504                     sector[ 1] != 0xFF ||
505                     sector[ 2] != 0xFF ||
506                     sector[ 3] != 0xFF ||
507                     sector[ 4] != 0xFF ||
508                     sector[ 5] != 0xFF ||
509                     sector[ 6] != 0xFF ||
510                     sector[ 7] != 0xFF ||
511                     sector[ 8] != 0xFF ||
512                     sector[ 9] != 0xFF ||
513                     sector[10] != 0xFF ||
514                     sector[11] != 0x00 )
515                {
516                    trackBlockSize = kCDSectorSizeCDDA;
517                    trackBlockType = kCDSectorTypeCDDA;
518                }
519            }
520        }
521
522        trackSize = trackBlockNext * trackBlockSize;
523
524        // Create a media object to represent this partition.
525
526        IOMedia * newMedia = instantiateMediaObject(
527                                 /* partition          */ &descriptor,
528                                 /* partitionSize      */ trackSize,
529                                 /* partitionBlockSize */ trackBlockSize,
530                                 /* partitionBlockType */ trackBlockType,
531                                 /* toc                */ toc );
532
533        if ( newMedia )
534        {
535            partitions->setObject(newMedia);
536            newMedia->release();
537        }
538    }
539
540    // Scan the ordered list of tracks.
541
542    for ( unsigned index = trackMinIndex; index <= trackMaxIndex; index++ )
543    {
544        CDTrack * track = tracks + index;
545        UInt32    trackBlockNext;
546        UInt64    trackSize;
547
548        // Determine whether this is an audio track or an unlinked data track.
549
550        if ( ( ( track->blockType != kCDSectorTypeCDDA       ) ) &&
551             ( ( track->blockType != kCDSectorTypeMode1      &&
552                 track->blockType != kCDSectorTypeMode2Form1 ) ||
553               ( trackMaxLinked                              ) ) )
554        {
555            continue;
556        }
557
558        // Determine where the partitions ends.
559
560        if ( index < trackMaxIndex && track->session == (track + 1)->session )
561        {
562            trackBlockNext = (track + 1)->block;
563        }
564        else
565        {
566            trackBlockNext = sessions[track->session].leadOut;
567        }
568
569        if ( track->block >= trackBlockNext )
570        {
571            goto scanErr;
572        }
573
574        trackSize = (trackBlockNext - track->block) * track->blockSize;
575
576        // Determine whether the partition is corrupt (fatal).
577
578        if ( isPartitionCorrupt( /* partition          */ track->descriptor,
579                                 /* partitionSize      */ trackSize,
580                                 /* partitionBlockSize */ track->blockSize,
581                                 /* partitionBlockType */ track->blockType,
582                                 /* toc                */ toc ) )
583        {
584            goto scanErr;
585        }
586
587        // Determine whether the partition is invalid (skipped).
588
589        if ( isPartitionInvalid( /* partition          */ track->descriptor,
590                                 /* partitionSize      */ trackSize,
591                                 /* partitionBlockSize */ track->blockSize,
592                                 /* partitionBlockType */ track->blockType,
593                                 /* toc                */ toc ) )
594        {
595            continue;
596        }
597
598        // Create a media object to represent this partition.
599
600        IOMedia * newMedia = instantiateMediaObject(
601                                 /* partition          */ track->descriptor,
602                                 /* partitionSize      */ trackSize,
603                                 /* partitionBlockSize */ track->blockSize,
604                                 /* partitionBlockType */ track->blockType,
605                                 /* toc                */ toc );
606
607        if ( newMedia )
608        {
609            partitions->setObject(newMedia);
610            newMedia->release();
611        }
612    }
613
614    // Release our resources.
615
616    media->close(this);
617    buffer->release();
618    IODelete(tracks, CDTrack, kCDTrackMaxIndex + 1);
619    IODelete(sessions, CDSession, kCDSessionMaxIndex + 1);
620
621    return partitions;
622
623scanErr:
624
625    // Release our resources.
626
627    if ( mediaIsOpen )  media->close(this);
628    if ( partitions )  partitions->release();
629    if ( buffer )  buffer->release();
630    if ( tracks )  IODelete(tracks, CDTrack, kCDTrackMaxIndex + 1);
631    if ( sessions )  IODelete(sessions, CDSession, kCDSessionMaxIndex + 1);
632
633    return 0;
634}
635
636bool IOCDPartitionScheme::isPartitionCorrupt(
637                                    CDTOCDescriptor * /* partition          */ ,
638                                    UInt64            /* partitionSize      */ ,
639                                    UInt32            /* partitionBlockSize */ ,
640                                    CDSectorType      /* partitionBlockType */ ,
641                                    CDTOC *           /* toc                */ )
642{
643    //
644    // Ask whether the given partition appears to be corrupt.  A partition that
645    // is corrupt will cause the failure of the CD partition scheme altogether.
646    //
647
648    return false;
649}
650
651bool IOCDPartitionScheme::isPartitionInvalid(
652                                    CDTOCDescriptor * partition,
653                                    UInt64            partitionSize,
654                                    UInt32            partitionBlockSize,
655                                    CDSectorType      partitionBlockType,
656                                    CDTOC *           toc )
657{
658    //
659    // Ask whether the given partition appears to be invalid.  A partition that
660    // is invalid will cause it to be skipped in the scan, but will not cause a
661    // failure of the CD partition scheme.
662    //
663
664    IOMedia * media          = getProvider();
665    UInt64    mediaBlockSize = media->getPreferredBlockSize();
666    UInt64    partitionBase  = 0;
667
668    // Compute the relative byte position and size of the new partition,
669    // relative to the provider media's natural blocking factor of 2352.
670
671    partitionBase = CDConvertMSFToClippedLBA(partition->p) * mediaBlockSize;
672    partitionSize = (partitionSize / partitionBlockSize) * mediaBlockSize;
673
674    // Determine whether the partition leaves the confines of the container.
675
676    if ( partitionBase + partitionSize > media->getSize() )  return true;
677
678    return false;
679}
680
681IOMedia * IOCDPartitionScheme::instantiateMediaObject(
682                                    CDTOCDescriptor * partition,
683                                    UInt64            partitionSize,
684                                    UInt32            partitionBlockSize,
685                                    CDSectorType      partitionBlockType,
686                                    CDTOC *           toc )
687{
688    //
689    // Instantiate a new media object to represent the given partition.
690    //
691
692    IOMedia * media               = getProvider();
693    UInt64    partitionBase       = 0;
694    char *    partitionHint       = 0;
695    bool      partitionIsWritable = media->isWritable();
696
697    // Compute the relative byte position of the new partition and encode it
698    // into the designated "logical space", given the partition's block type.
699    //
700    // 0x0000000000 through 0x00FFFFFFFF is the "don't care" space.
701    // 0x0100000000 through 0x01FFFFFFFF is the "audio" space.
702    // 0x0200000000 through 0x02FFFFFFFF is the "mode 1" space.
703    // 0x0300000000 through 0x03FFFFFFFF is the "mode 2 formless" space.
704    // 0x0400000000 through 0x04FFFFFFFF is the "mode 2 form 1" space.
705    // 0x0500000000 through 0x05FFFFFFFF is the "mode 2 form 2" space.
706
707    partitionBase  = CDConvertMSFToClippedLBA(partition->p);
708    partitionBase *= partitionBlockSize;
709    partitionBase += ((UInt64) partitionBlockType) << 32;
710
711    // Look up a type for the new partition.
712
713    OSDictionary * hintTable = OSDynamicCast(
714              /* type     */ OSDictionary,
715              /* instance */ getProperty(kIOCDPartitionSchemeContentTable) );
716
717    if ( hintTable )
718    {
719        char       hintIndex[5];
720        OSString * hintValue;
721
722        snprintf(hintIndex, sizeof(hintIndex), "0x%02X", partitionBlockType & 0xFF);
723
724        hintValue = OSDynamicCast(OSString, hintTable->getObject(hintIndex));
725
726        if ( hintValue ) partitionHint = (char *) hintValue->getCStringNoCopy();
727    }
728
729    // Determine whether the new partition is read-only.
730
731    if ( partition->point )  partitionIsWritable = false;
732
733    // Create the new media object.
734
735    IOMedia * newMedia = instantiateDesiredMediaObject(
736                                 /* partition          */ partition,
737                                 /* partitionSize      */ partitionSize,
738                                 /* partitionBlockSize */ partitionBlockSize,
739                                 /* partitionBlockType */ partitionBlockType,
740                                 /* toc                */ toc );
741
742    if ( newMedia )
743    {
744        if ( newMedia->init(
745                /* base               */ partitionBase,
746                /* size               */ partitionSize,
747                /* preferredBlockSize */ partitionBlockSize,
748                /* attributes         */ media->getAttributes(),
749                /* isWhole            */ false,
750                /* isWritable         */ partitionIsWritable,
751                /* contentHint        */ partitionHint ) )
752        {
753            // Set a name for this partition.
754
755            char name[24];
756            snprintf(name, sizeof(name), "Untitled %d", partition->point);
757            newMedia->setName(name);
758
759            // Set a location value (the partition number) for this partition.
760
761            char location[12];
762            snprintf(location, sizeof(location), "%d", partition->point);
763            newMedia->setLocation(location);
764
765            // Set the "Partition ID" key for this partition.
766
767            newMedia->setProperty(kIOMediaPartitionIDKey, partition->point, 32);
768
769            // Set the "Session ID" key for this partition.
770
771            newMedia->setProperty(kIOMediaSessionIDKey, partition->session, 32);
772        }
773        else
774        {
775            newMedia->release();
776            newMedia = 0;
777        }
778    }
779
780    return newMedia;
781}
782
783IOMedia * IOCDPartitionScheme::instantiateDesiredMediaObject(
784                                    CDTOCDescriptor * /* partition          */ ,
785                                    UInt64            /* partitionSize      */ ,
786                                    UInt32            /* partitionBlockSize */ ,
787                                    CDSectorType      /* partitionBlockType */ ,
788                                    CDTOC *           /* toc                */ )
789{
790    //
791    // Allocate a new media object (called from instantiateMediaObject).
792    //
793
794    return new IOMedia;
795}
796
797void IOCDPartitionScheme::read( IOService *           client,
798                                UInt64                byteStart,
799                                IOMemoryDescriptor *  buffer,
800                                IOStorageAttributes * attributes,
801                                IOStorageCompletion * completion )
802{
803    //
804    // Read data from the storage object at the specified byte offset into the
805    // specified buffer, asynchronously.   When the read completes, the caller
806    // will be notified via the specified completion action.
807    //
808    // The buffer will be retained for the duration of the read.
809    //
810    // For the CD partition scheme, we convert the read from a partition
811    // object into the appropriate readCD command to our provider media.
812    //
813
814    getProvider()->readCD( /* client     */ this,
815                           /* byteStart  */ (byteStart & 0xFFFFFFFF),
816                           /* buffer     */ buffer,
817                           /* sectorArea */ (CDSectorArea) kCDSectorAreaUser,
818                           /* sectorType */ (CDSectorType) (byteStart >> 32),
819#ifdef __LP64__
820                           /* attributes */ attributes,
821                           /* completion */ completion );
822#else /* !__LP64__ */
823                           /* completion */ completion ? *completion : (IOStorageCompletion) { 0 } );
824#endif /* !__LP64__ */
825}
826
827void IOCDPartitionScheme::write( IOService *           client,
828                                 UInt64                byteStart,
829                                 IOMemoryDescriptor *  buffer,
830                                 IOStorageAttributes * attributes,
831                                 IOStorageCompletion * completion )
832{
833    //
834    // Write data into the storage object at the specified byte offset from the
835    // specified buffer, asynchronously.   When the write completes, the caller
836    // will be notified via the specified completion action.
837    //
838    // The buffer will be retained for the duration of the write.
839    //
840    // For the CD partition scheme, we convert the write from a partition
841    // object into the appropriate writeCD command to our provider media.
842    //
843
844    getProvider()->writeCD( /* client     */ this,
845                            /* byteStart  */ (byteStart & 0xFFFFFFFF),
846                            /* buffer     */ buffer,
847                            /* sectorArea */ (CDSectorArea) kCDSectorAreaUser,
848                            /* sectorType */ (CDSectorType) (byteStart >> 32),
849#ifdef __LP64__
850                            /* attributes */ attributes,
851                            /* completion */ completion );
852#else /* !__LP64__ */
853                            /* completion */ completion ? *completion : (IOStorageCompletion) { 0 } );
854#endif /* !__LP64__ */
855}
856
857OSMetaClassDefineReservedUnused(IOCDPartitionScheme,  0);
858OSMetaClassDefineReservedUnused(IOCDPartitionScheme,  1);
859OSMetaClassDefineReservedUnused(IOCDPartitionScheme,  2);
860OSMetaClassDefineReservedUnused(IOCDPartitionScheme,  3);
861OSMetaClassDefineReservedUnused(IOCDPartitionScheme,  4);
862OSMetaClassDefineReservedUnused(IOCDPartitionScheme,  5);
863OSMetaClassDefineReservedUnused(IOCDPartitionScheme,  6);
864OSMetaClassDefineReservedUnused(IOCDPartitionScheme,  7);
865OSMetaClassDefineReservedUnused(IOCDPartitionScheme,  8);
866OSMetaClassDefineReservedUnused(IOCDPartitionScheme,  9);
867OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 10);
868OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 11);
869OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 12);
870OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 13);
871OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 14);
872OSMetaClassDefineReservedUnused(IOCDPartitionScheme, 15);
873