2 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
23 #include <IOKit/IOLib.h>
24 #include <IOKit/IOReturn.h>
25 #include <IOKit/IOMemoryDescriptor.h>
26 #include <IOKit/scsi/IOSCSIDeviceInterface.h>
27 #include <IOKit/storage/scsi/IOSCSICDDrive.h>
28 #include <IOKit/storage/scsi/IOSCSICDDriveNub.h>
30 #define super IOSCSIHDDrive
31 OSDefineMetaClassAndStructors(IOSCSICDDrive
,IOSCSIHDDrive
)
33 static void __inline
ConvertBCDToHex(UInt8
*value
)
35 *value
= (((*value
) >> 4) * 10) + ((*value
) & 0x0f);
38 /* The Callback (C) entry from the SCSI provider. We just glue
43 IOSCSICDDrive_gc_glue(IOService
*object
,void *param
)
46 struct IOBasicSCSI::context
*cx
;
48 self
= (IOSCSICDDrive
*)object
;
49 cx
= (struct IOBasicSCSI::context
*)param
;
50 self
->genericCompletion(cx
); /* do it in C++ */
54 IOSCSICDDrive::audioPause(bool pause
)
60 cx
= allocateContext();
62 return(kIOReturnNoMemory
);
65 bzero(&scsiCmd
, sizeof(scsiCmd
));
67 scsiCmd
.cdbLength
= 10;
68 scsiCmd
.cdb
[0] = kIOSCSICommandPauseResume
;
69 scsiCmd
.cdb
[8] = pause
? 0x00 : 0x01;
71 cx
->scsireq
->setCDB(&scsiCmd
);
72 cx
->scsireq
->setPointers(cx
->memory
, 0, false);
73 cx
->scsireq
->setPointers(cx
->senseDataDesc
, 255, false, true);
74 cx
->scsireq
->setTimeout(5000);
76 result
= simpleSynchIO(cx
);
84 IOSCSICDDrive::audioPlay(CDMSF timeStart
,CDMSF timeStop
)
86 return(doAudioPlayCommand(timeStart
,timeStop
));
90 IOSCSICDDrive::audioScan(CDMSF timeStart
,bool reverse
)
96 cx
= allocateContext();
98 return(kIOReturnNoMemory
);
101 bzero(&scsiCmd
, sizeof(scsiCmd
));
103 scsiCmd
.cdbLength
= 10;
104 scsiCmd
.cdb
[0] = 0xCD; /* AUDIO SCAN (10) */
105 scsiCmd
.cdb
[1] = reverse
? 0x10 : 0x00;
106 scsiCmd
.cdb
[3] = timeStart
.minute
;
107 scsiCmd
.cdb
[4] = timeStart
.second
;
108 scsiCmd
.cdb
[5] = timeStart
.frame
;
109 scsiCmd
.cdb
[9] = 0x40; /* MSF */
111 cx
->scsireq
->setCDB(&scsiCmd
);
112 cx
->scsireq
->setPointers(cx
->memory
, 0, false);
113 cx
->scsireq
->setPointers(cx
->senseDataDesc
, 255, false, true);
114 cx
->scsireq
->setTimeout(5000);
116 result
= simpleSynchIO(cx
);
124 IOSCSICDDrive::audioStop()
130 cx
= allocateContext();
132 return(kIOReturnNoMemory
);
135 bzero(&scsiCmd
, sizeof(scsiCmd
));
137 scsiCmd
.cdbLength
= 6;
138 scsiCmd
.cdb
[0] = 0x01; /* REZERO UNIT (6) */
140 cx
->scsireq
->setCDB(&scsiCmd
);
141 cx
->scsireq
->setPointers(cx
->memory
, 0, false);
142 cx
->scsireq
->setPointers(cx
->senseDataDesc
, 255, false, true);
143 cx
->scsireq
->setTimeout(5000);
145 result
= simpleSynchIO(cx
);
153 IOSCSICDDrive::deviceTypeMatches(UInt8 inqBuf
[],UInt32 inqLen
,SInt32
*score
)
155 if ((inqBuf
[0] & 0x1f) == kIOSCSIDeviceTypeCDROM
) {
156 // IOLog("%s[IOSCSICDDrive]::deviceTypeMatches, returning TRUE\n",getName());
160 // IOLog("%s[IOSCSICDDrive]::deviceTypeMatches, returning FALSE\n",getName());
161 return(false); /* we don't handle other devices */
166 IOSCSICDDrive::doAsyncReadCD(IOMemoryDescriptor
*buffer
,
167 UInt32 block
,UInt32 nblks
,
168 CDSectorArea sectorArea
,
169 CDSectorType sectorType
,
170 IOStorageCompletion completion
)
175 assert(buffer
->getDirection() == kIODirectionIn
);
177 bzero(&scsiCmd
, sizeof(scsiCmd
));
179 if (sectorArea
== kCDSectorAreaUser
) {
180 if (sectorType
== kCDSectorTypeCDDA
) {
181 scsiCmd
.cdbLength
= 12;
182 scsiCmd
.cdb
[ 0] = 0xD8; /* READ CD-DA */
183 scsiCmd
.cdb
[ 2] = (block
>> 24) & 0xFF;
184 scsiCmd
.cdb
[ 3] = (block
>> 16) & 0xFF;
185 scsiCmd
.cdb
[ 4] = (block
>> 8) & 0xFF;
186 scsiCmd
.cdb
[ 5] = (block
) & 0xFF;
187 scsiCmd
.cdb
[ 6] = (nblks
>> 24) & 0xFF;
188 scsiCmd
.cdb
[ 7] = (nblks
>> 16) & 0xFF;
189 scsiCmd
.cdb
[ 8] = (nblks
>> 8) & 0xFF;
190 scsiCmd
.cdb
[ 9] = (nblks
) & 0xFF;
191 } else if (sectorType
== kCDSectorTypeMode1
||
192 sectorType
== kCDSectorTypeMode2Form1
) {
193 return doAsyncReadWrite(buffer
,block
,nblks
,completion
);
197 if (scsiCmd
.cdbLength
== 0) {
198 return(kIOReturnUnsupported
);
201 cx
= allocateContext();
203 return(kIOReturnNoMemory
);
206 buffer
->retain(); /* bump the retain count */
208 cx
->completion
= completion
;
209 cx
->state
= kAsyncReadWrite
;
211 cx
->scsireq
->setCallback(this, (CallbackFn
)IOSCSICDDrive_gc_glue
, cx
);
212 cx
->scsireq
->setCDB(&scsiCmd
);
213 cx
->scsireq
->setPointers(buffer
, buffer
->getLength(), false);
214 cx
->scsireq
->setPointers(cx
->senseDataDesc
, 255, false, true);
215 cx
->scsireq
->setTimeout(60000);
217 /* Queue the request awaiting power and return. When power comes up,
218 * the request will be passed to standardAsyncReadWriteExecute.
220 queueCommand(cx
,kAsync
,getReadWritePowerState()); /* queue and possibly wait for power */
222 return(kIOReturnSuccess
);
226 IOSCSICDDrive::doAsyncReadWrite(IOMemoryDescriptor
*buffer
,
227 UInt32 block
,UInt32 nblks
,
228 IOStorageCompletion completion
)
230 if (buffer
->getDirection() == kIODirectionOut
) {
231 return(kIOReturnNotWritable
);
234 return(super::doAsyncReadWrite(buffer
,block
,nblks
,completion
));
238 IOSCSICDDrive::doSyncReadWrite(IOMemoryDescriptor
*buffer
,UInt32 block
,UInt32 nblks
)
240 if (buffer
->getDirection() == kIODirectionOut
) {
241 return(kIOReturnNotWritable
);
244 return(super::doSyncReadWrite(buffer
,block
,nblks
));
248 IOSCSICDDrive::doAudioPlayCommand(CDMSF timeStart
,CDMSF timeStop
)
251 struct IOAudioPlayMSFcdb
*p
;
256 cx
= allocateContext();
258 return(kIOReturnNoMemory
);
263 bzero( &scsiCDB
, sizeof(scsiCDB
) );
265 p
= (struct IOAudioPlayMSFcdb
*)scsiCDB
.cdb
; /* use PlayAudioMSF */
266 p
->opcode
= kIOSCSICommandPlayAudioMSF
;
269 p
->start_m
= timeStart
.minute
;
270 p
->start_s
= timeStart
.second
;
271 p
->start_f
= timeStart
.frame
;
272 p
->end_m
= timeStop
.minute
;
273 p
->end_s
= timeStop
.second
;
274 p
->end_f
= timeStop
.frame
;
277 scsiCDB
.cdbLength
= 10;
278 req
->setCDB( &scsiCDB
);
279 req
->setPointers(cx
->senseDataDesc
, 255, false, true);
281 req
->setPointers( cx
->memory
, 0, false );
282 req
->setTimeout( 5000 );
284 result
= simpleSynchIO(cx
);
292 IOSCSICDDrive::doFormatMedia(UInt64
/* byteCapacity */)
294 return(kIOReturnUnsupported
);
298 IOSCSICDDrive::doGetFormatCapacities(UInt64
* /* capacities */,UInt32
/* capacitiesMaxCount */) const
300 return(kIOReturnUnsupported
);
304 IOSCSICDDrive::doSynchronizeCache(void)
306 return(kIOReturnUnsupported
);
310 IOSCSICDDrive::getAudioStatus(CDAudioStatus
*status
)
315 /* Get a buffer for the returned data: */
317 result
= allocateTempBuffer(&tempBuf
,16);
318 if (result
!= kIOReturnSuccess
) {
319 return(kIOReturnNoMemory
);
322 result
= readSubChannel(tempBuf
,16,IORSCcdb::kCurrentPosition
,0);
324 if (result
== kIOReturnSuccess
) { /* we got the data */
325 assert(tempBuf
[2] == 0);
326 assert(tempBuf
[3] == 12);
327 assert(tempBuf
[4] == 1);
329 status
->status
= tempBuf
[ 1];
331 status
->position
.track
.number
= tempBuf
[ 6];
332 status
->position
.track
.index
= tempBuf
[ 7];
334 status
->position
.time
.minute
= tempBuf
[ 9];
335 status
->position
.time
.second
= tempBuf
[10];
336 status
->position
.time
.frame
= tempBuf
[11];
338 status
->position
.track
.time
.minute
= tempBuf
[13];
339 status
->position
.track
.time
.second
= tempBuf
[14];
340 status
->position
.track
.time
.frame
= tempBuf
[15];
342 deleteTempBuffer(tempBuf
,16);
348 IOSCSICDDrive::getAudioVolume(UInt8
*leftVolume
,UInt8
*rightVolume
)
353 UInt8 audio_control
[28];
355 cx
= allocateContext();
357 return(kIOReturnNoMemory
);
360 cx
->memory
= IOMemoryDescriptor::withAddress(audio_control
,
361 sizeof(audio_control
),
363 if (cx
->memory
== NULL
) {
365 return(kIOReturnNoMemory
);
368 bzero(&scsiCmd
, sizeof(scsiCmd
));
370 scsiCmd
.cdbLength
= 6;
371 scsiCmd
.cdb
[0] = 0x1a; /* MODE SENSE (6) */
372 scsiCmd
.cdb
[2] = 0x0e; /* PAGE CODE E */
373 scsiCmd
.cdb
[4] = sizeof(audio_control
);
375 cx
->scsireq
->setCDB(&scsiCmd
);
376 cx
->scsireq
->setPointers(cx
->memory
, sizeof(audio_control
), true);
377 cx
->scsireq
->setPointers(cx
->senseDataDesc
, 255, false, true);
378 cx
->scsireq
->setTimeout(5000);
380 result
= simpleSynchIO(cx
);
382 if (result
== kIOReturnSuccess
) {
383 assert((audio_control
[ 0] ) == 28-1);
384 assert((audio_control
[ 3] ) == 0x08);
385 assert((audio_control
[12] & 0x3f) == 0x0e);
386 assert((audio_control
[13] ) == 0x0e);
388 *leftVolume
= audio_control
[21];
389 *rightVolume
= audio_control
[23];
398 IOSCSICDDrive::getDeviceTypeName(void)
400 return(kIOBlockStorageDeviceTypeCDROM
);
404 IOSCSICDDrive::init(OSDictionary
* properties
)
406 return(super::init(properties
));
410 IOSCSICDDrive::instantiateNub(void)
414 /* Instantiate a generic CDROM nub so a generic driver can match above us. */
416 nub
= new IOSCSICDDriveNub
;
421 IOSCSICDDrive::mediaArrived(void)
426 IOSCSICDDrive::mediaGone(void)
431 IOSCSICDDrive::readISRC(UInt8 track
,CDISRC isrc
)
436 /* Get a buffer for the returned data: */
438 result
= allocateTempBuffer(&tempBuf
,24);
439 if (result
!= kIOReturnSuccess
) {
440 return(kIOReturnNoMemory
);
443 result
= readSubChannel(tempBuf
,24,IORSCcdb::kISRC
,track
);
444 if (result
== kIOReturnSuccess
) {
445 assert(tempBuf
[2] == 0);
446 assert(tempBuf
[3] == 20);
447 assert(tempBuf
[4] == 3);
449 if ((tempBuf
[8] & 0x80)) { /* return the ISRC */
450 bcopy(&tempBuf
[9],isrc
,kCDISRCMaxLength
);
451 isrc
[kCDISRCMaxLength
] = '\0';
453 result
= kIOReturnNotFound
;
457 deleteTempBuffer(tempBuf
,24);
463 IOSCSICDDrive::readMCN(CDMCN mcn
)
468 /* Get a buffer for the returned data: */
470 result
= allocateTempBuffer(&tempBuf
,24);
471 if (result
!= kIOReturnSuccess
) {
472 return(kIOReturnNoMemory
);
475 result
= readSubChannel(tempBuf
,24,IORSCcdb::kMCN
,0);
476 if (result
== kIOReturnSuccess
) {
477 assert(tempBuf
[2] == 0);
478 assert(tempBuf
[3] == 20);
479 assert(tempBuf
[4] == 2);
481 if ((tempBuf
[8] & 0x80)) { /* return the MCN */
482 bcopy(&tempBuf
[9],mcn
,kCDMCNMaxLength
);
483 mcn
[kCDMCNMaxLength
] = '\0';
485 result
= kIOReturnNotFound
;
489 deleteTempBuffer(tempBuf
,24);
496 IOSCSICDDrive::readSubChannel(UInt8
*buffer
,UInt32 length
,UInt8 dataFormat
,UInt8 trackNumber
)
504 cx
= allocateContext();
506 return(kIOReturnNoMemory
);
511 bzero( &scsiCDB
, sizeof(scsiCDB
) );
513 bzero(buffer
,length
);
515 c
= (struct IORSCcdb
*)(scsiCDB
.cdb
);
517 c
->opcode
= kIOSCSICommandReadSubChannel
;
519 c
->lunbits
|= IORSCcdb::kMSF
;
520 c
->subq
= IORSCcdb::kSubq
;
521 c
->dataformat
= dataFormat
;
522 c
->track
= trackNumber
; /* any valid track will do */
525 c
->len_hi
= length
>> 8;
526 c
->len_lo
= length
& 0xff;
529 scsiCDB
.cdbLength
= 10;
530 req
->setCDB( &scsiCDB
);
531 req
->setPointers(cx
->senseDataDesc
, 255, false, true);
533 cx
->memory
= IOMemoryDescriptor::withAddress((void *)buffer
,
536 req
->setPointers( cx
->memory
, length
, false );
538 req
->setTimeout( 5000 );
540 result
= simpleSynchIO(cx
);
548 IOSCSICDDrive::readTOC(IOMemoryDescriptor
*buffer
)
551 struct IOReadToccdb
*c
;
556 cx
= allocateContext();
558 return(kIOReturnNoMemory
);
563 bzero( &scsiCDB
, sizeof(scsiCDB
) );
565 c
= (struct IOReadToccdb
*)scsiCDB
.cdb
;
567 c
->opcode
= kIOSCSICommandReadTOC
;
568 c
->lunbits
= IOReadToccdb::kMSF
;
573 c
->start_trk_session
= 0;
574 c
->len_hi
= buffer
->getLength() >> 8;
575 c
->len_lo
= buffer
->getLength() & 0xff;
576 c
->ctlbyte
= IOReadToccdb::kFullTOC
<< 6; /* old format field */
578 scsiCDB
.cdbLength
= 10;
579 req
->setCDB( &scsiCDB
);
580 req
->setPointers(cx
->senseDataDesc
, 255, false, true);
582 buffer
->retain(); /* bump the retain count */
585 req
->setPointers( cx
->memory
, cx
->memory
->getLength(), false );
587 req
->setTimeout( 5000 );
589 result
= simpleSynchIO(cx
);
593 #ifdef HOLLYWOOD_BCD_TO_HEX_SUPPORT
594 IOByteCount tocMaxSize
;
595 CDTOC
*toc
= buffer
->getVirtualSegment(0, &tocMaxSize
);
597 /* Convert BCD-encoded values in TOC to hex values. */
598 if (toc
&& tocMaxSize
>= sizeof(UInt32
)) {
599 UInt32 count
= (tocMaxSize
- sizeof(UInt32
)) / sizeof(CDTOCDescriptor
);
600 for (UInt32 index
= 0; index
< count
; index
++) {
601 if (toc
->descriptors
[index
].point
<= 0x99) {
602 ConvertBCDToHex(&toc
->descriptors
[index
].point
);
604 if ((toc
->descriptors
[index
].point
& 0xf0) == 0xb0) {
605 ConvertBCDToHex(&toc
->descriptors
[index
].address
.minute
);
606 ConvertBCDToHex(&toc
->descriptors
[index
].address
.second
);
607 ConvertBCDToHex(&toc
->descriptors
[index
].address
.frame
);
608 ConvertBCDToHex(&toc
->descriptors
[index
].zero
);
610 if ( toc
->descriptors
[index
].point
<= 0x99 ||
611 ( toc
->descriptors
[index
].point
>= 0xa0 &&
612 toc
->descriptors
[index
].point
<= 0xc0 ) ) {
613 ConvertBCDToHex(&toc
->descriptors
[index
].p
.minute
);
614 if (toc
->descriptors
[index
].point
!= 0xa0) {
615 ConvertBCDToHex(&toc
->descriptors
[index
].p
.second
);
617 ConvertBCDToHex(&toc
->descriptors
[index
].p
.frame
);
621 #endif HOLLYWOOD_BCD_TO_HEX_SUPPORT
627 IOSCSICDDrive::reportMaxWriteTransfer(UInt64
/* blockSize */,UInt64
* /* max */)
633 IOSCSICDDrive::reportMediaState(bool *mediaPresent
,bool *changed
)
637 result
= super::reportMediaState(mediaPresent
,changed
);
639 if (result
!= kIOReturnSuccess
) {
640 IOLog("%s[IOSCSICDDrive]::reportMediaState; result=%s, changed = %s, present = %s\n",
641 getName(),stringFromReturn(result
),*changed
? "Y" : "N", *mediaPresent
? "Y" : "N");
644 if ((result
== kIOReturnSuccess
) && *changed
) { /* the media state changed */
645 if (*mediaPresent
) { /* new media inserted */
647 } else { /* media went away */
652 /* We don't return the result of our internal operations. But since they
653 * indicate a problem, we probably should report some kind of problem,
654 * or maybe just ignore the media change.
661 IOSCSICDDrive::reportWriteProtection(bool *isWriteProtected
)
663 *isWriteProtected
= true;
664 return(kIOReturnSuccess
);
668 IOSCSICDDrive::setAudioVolume(UInt8 leftVolume
,UInt8 rightVolume
)
673 UInt8 audio_control
[28];
675 cx
= allocateContext();
677 return(kIOReturnNoMemory
);
680 cx
->memory
= IOMemoryDescriptor::withAddress(audio_control
,
681 sizeof(audio_control
),
683 if (cx
->memory
== NULL
) {
685 return(kIOReturnNoMemory
);
688 /* Get current values. */
690 bzero(&scsiCmd
, sizeof(scsiCmd
));
692 scsiCmd
.cdbLength
= 6;
693 scsiCmd
.cdb
[0] = 0x1a; /* MODE SENSE (6) */
694 scsiCmd
.cdb
[2] = 0x0e; /* PAGE CODE E */
695 scsiCmd
.cdb
[4] = sizeof(audio_control
);
697 cx
->scsireq
->setCDB(&scsiCmd
);
698 cx
->scsireq
->setPointers(cx
->memory
, sizeof(audio_control
), true);
699 cx
->scsireq
->setPointers(cx
->senseDataDesc
, 255, false, true);
700 cx
->scsireq
->setTimeout(5000);
702 result
= simpleSynchIO(cx
);
704 if (result
== kIOReturnSuccess
) {
705 assert((audio_control
[ 0] ) == 28-1);
706 assert((audio_control
[ 3] ) == 0x08);
707 assert((audio_control
[12] & 0x3f) == 0x0e);
708 assert((audio_control
[13] ) == 0x0e);
710 /* Set new values. */
712 audio_control
[21] = audio_control
[25] = leftVolume
;
713 audio_control
[23] = audio_control
[27] = rightVolume
;
717 cx
= allocateContext();
719 return(kIOReturnNoMemory
);
722 cx
->memory
= IOMemoryDescriptor::withAddress(audio_control
,
723 sizeof(audio_control
),
725 if (cx
->memory
== NULL
) {
727 return(kIOReturnNoMemory
);
730 bzero(&scsiCmd
, sizeof(scsiCmd
));
732 scsiCmd
.cdbLength
= 6;
733 scsiCmd
.cdb
[0] = 0x15; /* MODE SELECT (6) */
734 scsiCmd
.cdb
[4] = sizeof(audio_control
);
736 cx
->scsireq
->setCDB(&scsiCmd
);
737 cx
->scsireq
->setPointers(cx
->memory
, sizeof(audio_control
), true);
738 cx
->scsireq
->setPointers(cx
->senseDataDesc
, 255, false, true);
739 cx
->scsireq
->setTimeout(5000);
741 result
= simpleSynchIO(cx
);