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/storage/IOCDBlockStorageDriver.h>
25#include <IOKit/storage/IOCDMedia.h>
26
27#define	super IOMedia
28OSDefineMetaClassAndStructors(IOCDMedia, IOMedia)
29
30class IOStorageSyncerLock
31{
32protected:
33
34    IOLock * _lock;
35
36public:
37
38    inline IOStorageSyncerLock( )
39    {
40        _lock = IOLockAlloc( );
41    }
42
43    inline ~IOStorageSyncerLock( )
44    {
45        if ( _lock ) IOLockFree( _lock );
46    }
47
48    inline void lock( )
49    {
50        IOLockLock( _lock );
51    }
52
53    inline void unlock( )
54    {
55        IOLockUnlock( _lock );
56    }
57
58    inline void sleep( void * event )
59    {
60        IOLockSleep( _lock, event, THREAD_UNINT );
61    }
62
63    inline void wakeup( void * event )
64    {
65        IOLockWakeup( _lock, event, false );
66    }
67};
68
69static IOStorageSyncerLock gIOStorageSyncerLock;
70
71class IOStorageSyncer
72{
73protected:
74
75    IOReturn _status;
76    bool     _wakeup;
77
78public:
79
80    IOStorageSyncer( )
81    {
82        _wakeup = false;
83    }
84
85    IOReturn wait( )
86    {
87        gIOStorageSyncerLock.lock( );
88
89        while ( _wakeup == false )
90        {
91            gIOStorageSyncerLock.sleep( this );
92        }
93
94        gIOStorageSyncerLock.unlock( );
95
96        return _status;
97    }
98
99    void signal( IOReturn status )
100    {
101        _status = status;
102
103        gIOStorageSyncerLock.lock( );
104
105        _wakeup = true;
106
107        gIOStorageSyncerLock.wakeup( this );
108
109        gIOStorageSyncerLock.unlock( );
110    }
111};
112
113static void storageCompletion(void *   target,
114                              void *   parameter,
115                              IOReturn status,
116                              UInt64   actualByteCount)
117{
118    //
119    // Internal completion routine for synchronous versions of read and write.
120    //
121
122    if (parameter)  *((UInt64 *)parameter) = actualByteCount;
123    ((IOStorageSyncer *)target)->signal(status);
124}
125
126IOCDBlockStorageDriver * IOCDMedia::getProvider() const
127{
128    //
129    // Obtain this object's provider.  We override the superclass's method to
130    // return a more specific subclass of IOService -- IOCDBlockStorageDriver.
131    // This method serves simply as a convenience to subclass developers.
132    //
133
134    return (IOCDBlockStorageDriver *) IOService::getProvider();
135}
136
137bool IOCDMedia::matchPropertyTable(OSDictionary * table, SInt32 * score)
138{
139    //
140    // Compare the properties in the supplied table to this object's properties.
141    //
142
143    // Ask our superclass' opinion.
144
145    if (super::matchPropertyTable(table, score) == false)  return false;
146
147    // We return success if the following expression is true -- individual
148    // comparisions evaluate to truth if the named property is not present
149    // in the supplied table.
150
151    return compareProperty(table, kIOCDMediaTOCKey ) &&
152           compareProperty(table, kIOCDMediaTypeKey);
153}
154
155void IOCDMedia::read(IOService *           /* client */,
156                     UInt64                byteStart,
157                     IOMemoryDescriptor *  buffer,
158                     IOStorageAttributes * attributes,
159                     IOStorageCompletion * completion)
160{
161    //
162    // Read data from the storage object at the specified byte offset into the
163    // specified buffer, asynchronously.   When the read completes, the caller
164    // will be notified via the specified completion action.
165    //
166    // The buffer will be retained for the duration of the read.
167    //
168    // This method will work even when the media is in the terminated state.
169    //
170
171    if (isInactive())
172    {
173        complete(completion, kIOReturnNoMedia);
174        return;
175    }
176
177    if (_openLevel == kIOStorageAccessNone)    // (instantaneous value, no lock)
178    {
179        complete(completion, kIOReturnNotOpen);
180        return;
181    }
182
183    if (_mediaSize == 0 || _preferredBlockSize == 0)
184    {
185        complete(completion, kIOReturnUnformattedMedia);
186        return;
187    }
188
189    if (buffer == 0)
190    {
191        complete(completion, kIOReturnBadArgument);
192        return;
193    }
194
195    if (_mediaSize < byteStart + buffer->getLength())
196    {
197        complete(completion, kIOReturnBadArgument);
198        return;
199    }
200
201    byteStart += _mediaBase;
202    getProvider()->readCD( /* client     */ this,
203                           /* byteStart  */ byteStart,
204                           /* buffer     */ buffer,
205                           /* sectorArea */ (CDSectorArea) 0xF8, // (2352 bytes)
206                           /* sectorType */ (CDSectorType) 0x00, // ( all types)
207#ifdef __LP64__
208                           /* attributes */ attributes,
209                           /* completion */ completion );
210#else /* !__LP64__ */
211                           /* completion */ completion ? *completion : (IOStorageCompletion) { 0 } );
212#endif /* !__LP64__ */
213}
214
215void IOCDMedia::write(IOService *           client,
216                      UInt64                byteStart,
217                      IOMemoryDescriptor *  buffer,
218                      IOStorageAttributes * attributes,
219                      IOStorageCompletion * completion)
220{
221    //
222    // Write data into the storage object at the specified byte offset from the
223    // specified buffer, asynchronously.   When the write completes, the caller
224    // will be notified via the specified completion action.
225    //
226    // The buffer will be retained for the duration of the write.
227    //
228    // This method will work even when the media is in the terminated state.
229    //
230
231    if (isInactive())
232    {
233        complete(completion, kIOReturnNoMedia);
234        return;
235    }
236
237    if (_openLevel == kIOStorageAccessNone)    // (instantaneous value, no lock)
238    {
239        complete(completion, kIOReturnNotOpen);
240        return;
241    }
242
243    if (_openLevel == kIOStorageAccessReader)  // (instantaneous value, no lock)
244    {
245        complete(completion, kIOReturnNotPrivileged);
246        return;
247    }
248
249    if (_isWritable == 0)
250    {
251        complete(completion, kIOReturnLockedWrite);
252        return;
253    }
254
255    if (_mediaSize == 0 || _preferredBlockSize == 0)
256    {
257        complete(completion, kIOReturnUnformattedMedia);
258        return;
259    }
260
261    if (buffer == 0)
262    {
263        complete(completion, kIOReturnBadArgument);
264        return;
265    }
266
267    if (_mediaSize < byteStart + buffer->getLength())
268    {
269        complete(completion, kIOReturnBadArgument);
270        return;
271    }
272
273    byteStart += _mediaBase;
274    getProvider()->writeCD(
275                           /* client     */ this,
276                           /* byteStart  */ byteStart,
277                           /* buffer     */ buffer,
278                           /* sectorArea */ (CDSectorArea) 0xF8, // (2352 bytes)
279                           /* sectorType */ (CDSectorType) 0x00, // ( all types)
280#ifdef __LP64__
281                           /* attributes */ attributes,
282                           /* completion */ completion );
283#else /* !__LP64__ */
284                           /* completion */ completion ? *completion : (IOStorageCompletion) { 0 } );
285#endif /* !__LP64__ */
286}
287
288IOReturn IOCDMedia::readCD(IOService *           client,
289                           UInt64                byteStart,
290                           IOMemoryDescriptor *  buffer,
291                           CDSectorArea          sectorArea,
292                           CDSectorType          sectorType,
293#ifdef __LP64__
294                           IOStorageAttributes * attributes,
295#endif /* __LP64__ */
296                           UInt64 *              actualByteCount)
297{
298    //
299    // Read data from the CD media object at the specified byte offset into the
300    // specified buffer, synchronously.   Special areas of the CD sector can be
301    // read via this method, such as the header and subchannel data.   When the
302    // read completes, this method will return to the caller.   The actual byte
303    // count field is optional.
304    //
305    // This method will work even when the media is in the terminated state.
306    //
307
308    IOStorageCompletion completion;
309    IOStorageSyncer     syncer;
310
311    // Fill in the completion information for this request.
312
313    completion.target    = &syncer;
314    completion.action    = storageCompletion;
315    completion.parameter = actualByteCount;
316
317    // Issue the asynchronous read.
318
319#ifdef __LP64__
320    readCD(client, byteStart, buffer, sectorArea, sectorType, attributes, &completion);
321#else /* !__LP64__ */
322    readCD(client, byteStart, buffer, sectorArea, sectorType, completion);
323#endif /* !__LP64__ */
324
325    // Wait for the read to complete.
326
327    return syncer.wait();
328}
329
330void IOCDMedia::readCD(IOService *           client,
331                       UInt64                byteStart,
332                       IOMemoryDescriptor *  buffer,
333                       CDSectorArea          sectorArea,
334                       CDSectorType          sectorType,
335#ifdef __LP64__
336                       IOStorageAttributes * attributes,
337                       IOStorageCompletion * completion)
338#else /* !__LP64__ */
339                       IOStorageCompletion   completion)
340#endif /* !__LP64__ */
341{
342    //
343    // Read data from the CD media object at the specified byte offset into the
344    // specified buffer, asynchronously.  Special areas of the CD sector can be
345    // read via this method, such as the header and subchannel data.   When the
346    // read completes, the caller will be notified via the specified completion
347    // action.
348    //
349    // The buffer will be retained for the duration of the read.
350    //
351    // This method will work even when the media is in the terminated state.
352    //
353
354    if (isInactive())
355    {
356        complete(completion, kIOReturnNoMedia);
357        return;
358    }
359
360    if (_openLevel == kIOStorageAccessNone)    // (instantaneous value, no lock)
361    {
362        complete(completion, kIOReturnNotOpen);
363        return;
364    }
365
366    if (_mediaSize == 0 || _preferredBlockSize == 0)
367    {
368        complete(completion, kIOReturnUnformattedMedia);
369        return;
370    }
371
372    if (buffer == 0)
373    {
374        complete(completion, kIOReturnBadArgument);
375        return;
376    }
377
378    byteStart += _mediaBase;
379    getProvider()->readCD( /* client     */ this,
380                           /* byteStart  */ byteStart,
381                           /* buffer     */ buffer,
382                           /* sectorArea */ sectorArea,
383                           /* sectorType */ sectorType,
384#ifdef __LP64__
385                           /* attributes */ attributes,
386#endif /* __LP64__ */
387                           /* completion */ completion );
388}
389
390IOReturn IOCDMedia::readISRC(UInt8 track, CDISRC isrc)
391{
392    //
393    // Read the International Standard Recording Code for the specified track.
394    //
395    // This method will work even when the media is in the terminated state.
396    //
397
398    if (isInactive())
399    {
400        return kIOReturnNoMedia;
401    }
402
403    return getProvider()->readISRC(track, isrc);
404}
405
406IOReturn IOCDMedia::readMCN(CDMCN mcn)
407{
408    //
409    // Read the Media Catalog Number (also known as the Universal Product Code).
410    //
411    // This method will work even when the media is in the terminated state.
412    //
413
414    if (isInactive())
415    {
416        return kIOReturnNoMedia;
417    }
418
419    return getProvider()->readMCN(mcn);
420}
421
422CDTOC * IOCDMedia::getTOC()
423{
424    //
425    // Get the full Table Of Contents.
426    //
427    // This method will work even when the media is in the terminated state.
428    //
429
430    if (isInactive())
431    {
432        return 0;
433    }
434
435    return getProvider()->getTOC();
436}
437
438IOReturn IOCDMedia::getSpeed(UInt16 * kilobytesPerSecond)
439{
440    //
441    // Get the current speed used for data transfers.
442    //
443
444    if (isInactive())
445    {
446        return kIOReturnNoMedia;
447    }
448
449    return getProvider()->getSpeed(kilobytesPerSecond);
450}
451
452IOReturn IOCDMedia::setSpeed(UInt16 kilobytesPerSecond)
453{
454    //
455    // Set the speed to be used for data transfers.
456    //
457
458    if (isInactive())
459    {
460        return kIOReturnNoMedia;
461    }
462
463    return getProvider()->setSpeed(kilobytesPerSecond);
464}
465
466IOReturn IOCDMedia::readTOC(IOMemoryDescriptor * buffer,
467                            CDTOCFormat          format,
468                            UInt8                formatAsTime,
469                            UInt8                trackOrSessionNumber,
470                            UInt16 *             actualByteCount)
471{
472    if (isInactive())
473    {
474        if (actualByteCount)  *actualByteCount = 0;
475
476        return kIOReturnNoMedia;
477    }
478
479    if (buffer == 0)
480    {
481        if (actualByteCount)  *actualByteCount = 0;
482
483        return kIOReturnBadArgument;
484    }
485
486    return getProvider()->readTOC(
487                                /* buffer               */ buffer,
488                                /* format               */ format,
489                                /* formatAsTime         */ formatAsTime,
490                                /* trackOrSessionNumber */ trackOrSessionNumber,
491                                /* actualByteCount      */ actualByteCount );
492}
493
494IOReturn IOCDMedia::readDiscInfo(IOMemoryDescriptor * buffer,
495                                 UInt16 *             actualByteCount)
496{
497    if (isInactive())
498    {
499        if (actualByteCount)  *actualByteCount = 0;
500
501        return kIOReturnNoMedia;
502    }
503
504    if (buffer == 0)
505    {
506        if (actualByteCount)  *actualByteCount = 0;
507
508        return kIOReturnBadArgument;
509    }
510
511    return getProvider()->readDiscInfo(
512                                /* buffer               */ buffer,
513                                /* actualByteCount      */ actualByteCount );
514}
515
516IOReturn IOCDMedia::readTrackInfo(IOMemoryDescriptor *   buffer,
517                                  UInt32                 address,
518                                  CDTrackInfoAddressType addressType,
519                                  UInt16 *               actualByteCount)
520{
521    if (isInactive())
522    {
523        if (actualByteCount)  *actualByteCount = 0;
524
525        return kIOReturnNoMedia;
526    }
527
528    if (buffer == 0)
529    {
530        if (actualByteCount)  *actualByteCount = 0;
531
532        return kIOReturnBadArgument;
533    }
534
535    return getProvider()->readTrackInfo(
536                                /* buffer               */ buffer,
537                                /* address              */ address,
538                                /* addressType          */ addressType,
539                                /* actualByteCount      */ actualByteCount );
540}
541
542void IOCDMedia::writeCD(IOService *           client,
543                        UInt64                byteStart,
544                        IOMemoryDescriptor *  buffer,
545                        CDSectorArea          sectorArea,
546                        CDSectorType          sectorType,
547#ifdef __LP64__
548                        IOStorageAttributes * attributes,
549                        IOStorageCompletion * completion)
550#else /* !__LP64__ */
551                        IOStorageCompletion   completion)
552#endif /* !__LP64__ */
553{
554    //
555    // Write data into the CD media object at the specified byte offset from the
556    // specified buffer, asynchronously.    When the write completes, the caller
557    // will be notified via the specified completion action.
558    //
559    // The buffer will be retained for the duration of the write.
560    //
561    // This method will work even when the media is in the terminated state.
562    //
563
564    if (isInactive())
565    {
566        complete(completion, kIOReturnNoMedia);
567        return;
568    }
569
570    if (_openLevel == kIOStorageAccessNone)    // (instantaneous value, no lock)
571    {
572        complete(completion, kIOReturnNotOpen);
573        return;
574    }
575
576    if (_openLevel == kIOStorageAccessReader)  // (instantaneous value, no lock)
577    {
578        complete(completion, kIOReturnNotPrivileged);
579        return;
580    }
581
582    if (_isWritable == 0)
583    {
584        complete(completion, kIOReturnLockedWrite);
585        return;
586    }
587
588    if (_mediaSize == 0 || _preferredBlockSize == 0)
589    {
590        complete(completion, kIOReturnUnformattedMedia);
591        return;
592    }
593
594    if (buffer == 0)
595    {
596        complete(completion, kIOReturnBadArgument);
597        return;
598    }
599
600    byteStart += _mediaBase;
601    getProvider()->writeCD( /* client     */ this,
602                            /* byteStart  */ byteStart,
603                            /* buffer     */ buffer,
604                            /* sectorArea */ sectorArea,
605                            /* sectorType */ sectorType,
606#ifdef __LP64__
607                            /* attributes */ attributes,
608#endif /* __LP64__ */
609                            /* completion */ completion );
610}
611
612IOReturn IOCDMedia::writeCD(IOService *           client,
613                            UInt64                byteStart,
614                            IOMemoryDescriptor *  buffer,
615                            CDSectorArea          sectorArea,
616                            CDSectorType          sectorType,
617#ifdef __LP64__
618                            IOStorageAttributes * attributes,
619#endif /* __LP64__ */
620                            UInt64 *              actualByteCount)
621{
622    //
623    // Write data into the CD media object at the specified byte offset from the
624    // specified buffer, synchronously.    When the write completes, this method
625    // will return to the caller.  The actual byte count field is optional.
626    //
627    // This method will work even when the media is in the terminated state.
628    //
629
630    IOStorageCompletion completion;
631    IOStorageSyncer     syncer;
632
633    // Fill in the completion information for this request.
634
635    completion.target    = &syncer;
636    completion.action    = storageCompletion;
637    completion.parameter = actualByteCount;
638
639    // Issue the asynchronous write.
640
641#ifdef __LP64__
642    writeCD(client, byteStart, buffer, sectorArea, sectorType, attributes, &completion);
643#else /* !__LP64__ */
644    writeCD(client, byteStart, buffer, sectorArea, sectorType, completion);
645#endif /* !__LP64__ */
646
647    // Wait for the write to complete.
648
649    return syncer.wait();
650}
651
652#ifdef __LP64__
653OSMetaClassDefineReservedUnused(IOCDMedia,  0);
654OSMetaClassDefineReservedUnused(IOCDMedia,  1);
655OSMetaClassDefineReservedUnused(IOCDMedia,  2);
656OSMetaClassDefineReservedUnused(IOCDMedia,  3);
657OSMetaClassDefineReservedUnused(IOCDMedia,  4);
658OSMetaClassDefineReservedUnused(IOCDMedia,  5);
659OSMetaClassDefineReservedUnused(IOCDMedia,  6);
660#else /* !__LP64__ */
661OSMetaClassDefineReservedUsed(IOCDMedia,  0);
662OSMetaClassDefineReservedUsed(IOCDMedia,  1);
663OSMetaClassDefineReservedUsed(IOCDMedia,  2);
664OSMetaClassDefineReservedUsed(IOCDMedia,  3);
665OSMetaClassDefineReservedUsed(IOCDMedia,  4);
666OSMetaClassDefineReservedUsed(IOCDMedia,  5);
667OSMetaClassDefineReservedUsed(IOCDMedia,  6);
668#endif /* !__LP64__ */
669OSMetaClassDefineReservedUnused(IOCDMedia,  7);
670OSMetaClassDefineReservedUnused(IOCDMedia,  8);
671OSMetaClassDefineReservedUnused(IOCDMedia,  9);
672OSMetaClassDefineReservedUnused(IOCDMedia, 10);
673OSMetaClassDefineReservedUnused(IOCDMedia, 11);
674OSMetaClassDefineReservedUnused(IOCDMedia, 12);
675OSMetaClassDefineReservedUnused(IOCDMedia, 13);
676OSMetaClassDefineReservedUnused(IOCDMedia, 14);
677OSMetaClassDefineReservedUnused(IOCDMedia, 15);
678