]> git.saurik.com Git - apple/xnu.git/blob - iokit/Families/IOCDStorage/IOCDBlockStorageDriver.cpp
1e15e5ff184b839f8b062a40874f7e881b8a15eb
[apple/xnu.git] / iokit / Families / IOCDStorage / IOCDBlockStorageDriver.cpp
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 #include <IOKit/IOBufferMemoryDescriptor.h>
23 #include <IOKit/IOLib.h>
24 #include <IOKit/storage/IOCDBlockStorageDriver.h>
25 #include <IOKit/storage/IOCDMedia.h>
26 #include <IOKit/storage/IOCDAudioControl.h>
27 #include <IOKit/storage/IOCDBlockStorageDevice.h>
28 #include <libkern/OSByteOrder.h>
29
30
31 // Hack for Cheetah to prevent sleep if there's disk activity.
32 static IOService * gIORootPowerDomain = NULL;
33
34 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
35
36 #define super IOBlockStorageDriver
37 OSDefineMetaClassAndStructors(IOCDBlockStorageDriver,IOBlockStorageDriver)
38
39 IOCDBlockStorageDevice *
40 IOCDBlockStorageDriver::getProvider() const
41 {
42 return (IOCDBlockStorageDevice *) IOService::getProvider();
43 }
44
45
46 /* Accept a new piece of media, doing whatever's necessary to make it
47 * show up properly to the system. The arbitration lock is assumed to
48 * be held during the call.
49 */
50 IOReturn
51 IOCDBlockStorageDriver::acceptNewMedia(void)
52 {
53 IOReturn result;
54 bool ok;
55 int i;
56 UInt64 nblocks;
57 int nentries;
58 int nDataTracks;
59 int nAudioTracks;
60 char name[128];
61 bool nameSep;
62
63 /* First, we cache information about the tracks on the disc: */
64
65 result = cacheTocInfo();
66 if (result != kIOReturnSuccess) {
67 assert(_toc == NULL);
68 }
69
70 /* Scan thru the track list, counting up the number of Data and Audio tracks. */
71
72 nDataTracks = 0;
73 nAudioTracks = 0;
74 nblocks = 0;
75
76 if (_toc) {
77 nentries = (_toc->length - sizeof(UInt16)) / sizeof(CDTOCDescriptor);
78
79 for (i = 0; i < nentries; i++) {
80 /* tracks 1-99, not leadout or skip intervals */
81 if (_toc->descriptors[i].point <= 99 && _toc->descriptors[i].adr == 1) {
82 if ((_toc->descriptors[i].control & 0x04)) {
83 /* it's a data track */
84 nDataTracks++;
85 } else {
86 nAudioTracks++;
87 }
88 /* leadout */
89 } else if (_toc->descriptors[i].point == 0xA2 && _toc->descriptors[i].adr == 1) {
90 if (nblocks < CDConvertMSFToLBA(_toc->descriptors[i].p)) {
91 nblocks = CDConvertMSFToLBA(_toc->descriptors[i].p);
92 }
93 }
94 }
95
96 if (nblocks < _maxBlockNumber + 1) {
97 nblocks = _maxBlockNumber + 1;
98 }
99 } else if (_maxBlockNumber) {
100 nblocks = _maxBlockNumber + 1;
101 }
102
103 /* Instantiate a CD Media nub above ourselves. */
104
105 name[0] = 0;
106 nameSep = false;
107 if (getProvider()->getVendorString()) {
108 strcat(name, getProvider()->getVendorString());
109 nameSep = true;
110 }
111 if (getProvider()->getProductString()) {
112 if (nameSep == true) strcat(name, " ");
113 strcat(name, getProvider()->getProductString());
114 nameSep = true;
115 }
116 if (nameSep == true) strcat(name, " ");
117 strcat(name, "Media");
118
119 _mediaObject = instantiateMediaObject(0,nblocks*kBlockSizeCD,kBlockSizeCD,name);
120 result = (_mediaObject) ? kIOReturnSuccess : kIOReturnBadArgument;
121
122 if (result == kIOReturnSuccess) {
123 ok = _mediaObject->attach(this);
124 } else {
125 IOLog("%s[IOCDBlockStorageDriver]::acceptNewMedia; can't instantiate CD media nub.\n",getName());
126 return(result); /* give up now */
127 }
128 if (!ok) {
129 IOLog("%s[IOCDBlockStorageDriver]::acceptNewMedia; can't attach CD media nub.\n",getName());
130 _mediaObject->release();
131 _mediaObject = NULL;
132 return(kIOReturnNoMemory); /* give up now */
133 }
134
135 /* Instantiate an audio control nub for the audio portion of the media. */
136
137 if (nAudioTracks) {
138 _acNub = new IOCDAudioControl;
139 if (_acNub) {
140 _acNub->init();
141 ok = _acNub->attach(this);
142 if (!ok) {
143 IOLog("%s[IOCDBlockStorageDriver]::acceptNewMedia; can't attach audio control nub.\n",getName());
144 _acNub->release();
145 _acNub = NULL;
146 }
147 } else {
148 IOLog("%s[IOCDBlockStorageDriver]::acceptNewMedia; can't instantiate audio control nub.\n",
149 getName());
150 }
151 }
152
153 /* Now that the nubs are attached, register them. */
154
155 _mediaPresent = true;
156 if (_toc) {
157 _mediaObject->setProperty(kIOCDMediaTOCKey,(void*)_toc,_tocSize);
158 }
159 _mediaObject->registerService();
160
161 if (_acNub) {
162 _acNub->registerService();
163 }
164
165 return(result);
166 }
167
168 IOReturn
169 IOCDBlockStorageDriver::audioPause(bool pause)
170 {
171 return(getProvider()->audioPause(pause));
172 }
173
174 IOReturn
175 IOCDBlockStorageDriver::audioPlay(CDMSF timeStart,CDMSF timeStop)
176 {
177 return(getProvider()->audioPlay(timeStart,timeStop));
178 }
179
180 IOReturn
181 IOCDBlockStorageDriver::audioScan(CDMSF timeStart,bool reverse)
182 {
183 return(getProvider()->audioScan(timeStart,reverse));
184 }
185
186 IOReturn
187 IOCDBlockStorageDriver::audioStop()
188 {
189 return(getProvider()->audioStop());
190 }
191
192 IOReturn
193 IOCDBlockStorageDriver::cacheTocInfo(void)
194 {
195 IOBufferMemoryDescriptor *buffer = NULL;
196 IOReturn result;
197 CDTOC *toc;
198 UInt16 tocSize;
199
200 assert(sizeof(CDTOC) == 4); /* (compiler/platform check) */
201 assert(sizeof(CDTOCDescriptor) == 11); /* (compiler/platform check) */
202
203 assert(_toc == NULL);
204
205 /* Read the TOC header: */
206
207 buffer = IOBufferMemoryDescriptor::withCapacity(sizeof(CDTOC),kIODirectionIn);
208 if (buffer == NULL) {
209 return(kIOReturnNoMemory);
210 }
211
212 result = getProvider()->readTOC(buffer);
213 if (result != kIOReturnSuccess) {
214 buffer->release();
215 return(result);
216 }
217
218 toc = (CDTOC *) buffer->getBytesNoCopy();
219 tocSize = OSSwapBigToHostInt16(toc->length) + sizeof(UInt16);
220
221 buffer->release();
222
223 /* Read the TOC in full: */
224
225 buffer = IOBufferMemoryDescriptor::withCapacity(tocSize,kIODirectionIn);
226 if (buffer == NULL) {
227 return(kIOReturnNoMemory);
228 }
229
230 result = getProvider()->readTOC(buffer);
231 if (result != kIOReturnSuccess) {
232 buffer->release();
233 return(result);
234 }
235
236 toc = (CDTOC *) IOMalloc(tocSize);
237 if (toc == NULL) {
238 buffer->release();
239 return(kIOReturnNoMemory);
240 }
241
242 if (buffer->readBytes(0,toc,tocSize) != tocSize) {
243 buffer->release();
244 IOFree(toc,tocSize);
245 return(kIOReturnNoMemory);
246 }
247
248 _toc = toc;
249 _tocSize = tocSize;
250
251 buffer->release();
252
253 /* Convert big-endian values in TOC to host-endianess: */
254
255 if (_tocSize >= sizeof(UInt16)) {
256 _toc->length = OSSwapBigToHostInt16(_toc->length);
257 }
258
259 return(result);
260 }
261
262 /* Decommission all nubs. The arbitration lock is assumed to
263 * be held during the call.
264 */
265 IOReturn
266 IOCDBlockStorageDriver::decommissionMedia(bool forcible)
267 {
268 IOReturn result;
269
270 if (_mediaObject) {
271 /* If this is a forcible decommission (i.e. media is gone), we don't
272 * care whether the teardown worked; we forget about the media.
273 */
274 if (_mediaObject->terminate(forcible ? kIOServiceRequired : 0) || forcible) {
275 _mediaObject->release();
276 _mediaObject = 0;
277
278 initMediaState(); /* clear all knowledge of the media */
279 result = kIOReturnSuccess;
280
281 } else {
282 result = kIOReturnBusy;
283 }
284 } else {
285 result = kIOReturnNoMedia;
286 }
287
288 /* We only attempt to decommission the audio portion of the
289 * CD if all the data tracks decommissioned successfully.
290 */
291
292 if (result == kIOReturnSuccess) {
293 if (_acNub) {
294 _acNub->terminate(kIOServiceRequired);
295 _acNub->release();
296 _acNub = 0;
297 }
298 if (_toc) {
299 IOFree(_toc,_tocSize);
300 _toc = NULL;
301 _tocSize = 0;
302 }
303 }
304
305 return(result);
306 }
307
308 /* We should check with other clients using the other nubs before we allow
309 * the client of the IOCDMedia to eject the media.
310 */
311 IOReturn
312 IOCDBlockStorageDriver::ejectMedia(void)
313 {
314 /* For now, we don't check with the other clients. */
315
316 return(super::ejectMedia());
317 }
318
319 void
320 IOCDBlockStorageDriver::executeRequest(UInt64 byteStart,
321 IOMemoryDescriptor *buffer,
322 IOStorageCompletion completion,
323 IOBlockStorageDriver::Context *context)
324 {
325 UInt32 block;
326 UInt32 nblks;
327 IOReturn result;
328
329 if (!_mediaPresent) { /* no media? you lose */
330 complete(completion, kIOReturnNoMedia,0);
331 return;
332 }
333
334 /* We know that we are never called with a request too large,
335 * nor one that is misaligned with a block.
336 */
337 assert((byteStart % context->block.size) == 0);
338 assert((buffer->getLength() % context->block.size) == 0);
339
340 block = byteStart / context->block.size;
341 nblks = buffer->getLength() / context->block.size;
342
343 /* Now the protocol-specific provider implements the actual
344 * start of the data transfer: */
345
346 // Tickle the root power domain to reset the sleep countdown.
347 if (gIORootPowerDomain) {
348 gIORootPowerDomain->activityTickle(kIOPMSubclassPolicy);
349 }
350
351 if (context->block.type == kBlockTypeCD) {
352 result = getProvider()->doAsyncReadCD(buffer,block,nblks,
353 (CDSectorArea)context->block.typeSub[0],
354 (CDSectorType)context->block.typeSub[1],
355 completion);
356 } else {
357 result = getProvider()->doAsyncReadWrite(buffer,block,nblks,completion);
358 }
359
360 if (result != kIOReturnSuccess) { /* it failed to start */
361 complete(completion,result);
362 return;
363 }
364 }
365
366 IOReturn
367 IOCDBlockStorageDriver::getAudioStatus(CDAudioStatus *status)
368 {
369 return(getProvider()->getAudioStatus(status));
370 }
371
372 IOReturn
373 IOCDBlockStorageDriver::getAudioVolume(UInt8 *leftVolume,UInt8 *rightVolume)
374 {
375 return(getProvider()->getAudioVolume(leftVolume,rightVolume));
376 }
377
378 const char *
379 IOCDBlockStorageDriver::getDeviceTypeName(void)
380 {
381 return(kIOBlockStorageDeviceTypeCDROM);
382 }
383
384 UInt64
385 IOCDBlockStorageDriver::getMediaBlockSize(CDSectorArea area,CDSectorType type)
386 {
387 UInt64 blockSize = 0;
388
389 const SInt16 areaSize[kCDSectorTypeCount][8] =
390 { /* 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 */
391 /* Unknown */ { 96, 294, -1, 280, 2048, 4, 8, 12 },
392 /* CDDA */ { 96, 294, -1, 0, 2352, 0, 0, 0 },
393 /* Mode1 */ { 96, 294, -1, 288, 2048, 4, 0, 12 },
394 /* Mode2 */ { 96, 294, -1, 0, 2336, 4, 0, 12 },
395 /* Mode2Form1 */ { 96, 294, -1, 280, 2048, 4, 8, 12 },
396 /* Mode2Form2 */ { 96, 294, -1, 0, 2328, 4, 8, 12 },
397 };
398
399 if ( type >= kCDSectorTypeCount ) return 0;
400
401 for ( UInt32 index = 0; index < 8; index++ )
402 {
403 if ( ((area >> index) & 0x01) )
404 {
405 if ( areaSize[type][index] == -1 ) return 0;
406 blockSize += areaSize[type][index];
407 }
408 }
409
410 return blockSize;
411 }
412
413 UInt32
414 IOCDBlockStorageDriver::getMediaType(void)
415 {
416 return(getProvider()->getMediaType());
417 }
418
419 CDTOC *
420 IOCDBlockStorageDriver::getTOC(void)
421 {
422 return(_toc);
423 }
424
425 bool
426 IOCDBlockStorageDriver::init(OSDictionary * properties)
427 {
428 _acNub = NULL;
429 _toc = NULL;
430 _tocSize = 0;
431
432 // Hack for Cheetah to prevent sleep if there's disk activity.
433 if (!gIORootPowerDomain) {
434 // No danger of race here as we're ultimately just setting
435 // the gIORootPowerDomain variable.
436
437 do {
438 IOService * root = NULL;
439 OSIterator * iterator = NULL;
440 OSDictionary * pmDict = NULL;
441
442 root = IOService::getServiceRoot();
443 if (!root) break;
444
445 pmDict = root->serviceMatching("IOPMrootDomain");
446 if (!pmDict) break;
447
448 iterator = root->getMatchingServices(pmDict);
449 pmDict->release();
450 if (!iterator) break;
451
452 if (iterator) {
453 gIORootPowerDomain = OSDynamicCast(IOService, iterator->getNextObject());
454 iterator->release();
455 }
456 } while (false);
457 }
458
459 return(super::init(properties));
460 }
461
462 IOMedia *
463 IOCDBlockStorageDriver::instantiateDesiredMediaObject(void)
464 {
465 return(new IOCDMedia);
466 }
467
468 IOMedia *
469 IOCDBlockStorageDriver::instantiateMediaObject(UInt64 base,UInt64 byteSize,
470 UInt32 blockSize,char *mediaName)
471 {
472 IOMedia *media;
473
474 media = super::instantiateMediaObject(base,byteSize,blockSize,mediaName);
475
476 if (media) {
477 char *description = NULL;
478
479 switch (getMediaType()) {
480 case kCDMediaTypeROM:
481 description = kIOCDMediaTypeROM;
482 break;
483 case kCDMediaTypeR:
484 description = kIOCDMediaTypeR;
485 break;
486 case kCDMediaTypeRW:
487 description = kIOCDMediaTypeRW;
488 break;
489 }
490
491 if (description) {
492 media->setProperty(kIOCDMediaTypeKey, description);
493 }
494 }
495
496 return media;
497 }
498
499 void
500 IOCDBlockStorageDriver::readCD(IOService *client,
501 UInt64 byteStart,
502 IOMemoryDescriptor *buffer,
503 CDSectorArea sectorArea,
504 CDSectorType sectorType,
505 IOStorageCompletion completion)
506 {
507 assert(buffer->getDirection() == kIODirectionIn);
508
509 prepareRequest(byteStart, buffer, sectorArea, sectorType, completion);
510 }
511
512 void
513 IOCDBlockStorageDriver::prepareRequest(UInt64 byteStart,
514 IOMemoryDescriptor *buffer,
515 CDSectorArea sectorArea,
516 CDSectorType sectorType,
517 IOStorageCompletion completion)
518 {
519 Context * context;
520 IOReturn status;
521
522 // Allocate a context structure to hold some of our state.
523
524 context = allocateContext();
525
526 if (context == 0)
527 {
528 complete(completion, kIOReturnNoMemory);
529 return;
530 }
531
532 // Prepare the transfer buffer.
533
534 status = buffer->prepare();
535
536 if (status != kIOReturnSuccess)
537 {
538 deleteContext(context);
539 complete(completion, status);
540 return;
541 }
542
543 // Fill in the context structure with some of our state.
544
545 if ( ( sectorArea == kCDSectorAreaUser ) &&
546 ( sectorType == kCDSectorTypeMode1 ||
547 sectorType == kCDSectorTypeMode2Form1 ) )
548 {
549 context->block.size = _mediaBlockSize;
550 context->block.type = kBlockTypeStandard;
551 }
552 else
553 {
554 context->block.size = getMediaBlockSize(sectorArea, sectorType);
555 context->block.type = kBlockTypeCD;
556 context->block.typeSub[0] = sectorArea;
557 context->block.typeSub[1] = sectorType;
558 }
559
560 context->original.byteStart = byteStart;
561 context->original.buffer = buffer;
562 context->original.buffer->retain();
563 context->original.completion = completion;
564
565 completion.target = this;
566 completion.action = prepareRequestCompletion;
567 completion.parameter = context;
568
569 // Deblock the transfer.
570
571 deblockRequest(byteStart, buffer, completion, context);
572 }
573
574 IOReturn
575 IOCDBlockStorageDriver::readISRC(UInt8 track,CDISRC isrc)
576 {
577 return(getProvider()->readISRC(track,isrc));
578 }
579
580 IOReturn
581 IOCDBlockStorageDriver::readMCN(CDMCN mcn)
582 {
583 return(getProvider()->readMCN(mcn));
584 }
585
586 IOReturn
587 IOCDBlockStorageDriver::setAudioVolume(UInt8 leftVolume,UInt8 rightVolume)
588 {
589 return(getProvider()->setAudioVolume(leftVolume,rightVolume));
590 }
591
592 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 0);
593 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 1);
594 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 2);
595 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 3);
596 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 4);
597 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 5);
598 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 6);
599 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 7);
600 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 8);
601 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 9);
602 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 10);
603 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 11);
604 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 12);
605 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 13);
606 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 14);
607 OSMetaClassDefineReservedUnused(IOCDBlockStorageDriver, 15);