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/scsi/IOSCSIDeviceInterface.h>
26 #include <IOKit/storage/scsi/IOSCSIHDDrive.h>
27 #include <IOKit/storage/scsi/IOSCSIHDDriveNub.h>
29 #define super IOBasicSCSI
30 OSDefineMetaClassAndStructors(IOSCSIHDDrive
,IOBasicSCSI
)
33 IOSCSIHDDrive::allocateFormatBuffer(UInt8
**buf
,UInt32
*len
)
35 /* The default implementation uses no buffer. */
39 return(kIOReturnSuccess
);
43 IOSCSIHDDrive::composeFormatBuffer(UInt8
* /* buf */,UInt32
/* buflen */)
45 return(0); /* default: no fmtdata buffer to transfer */
49 IOSCSIHDDrive::constructDeviceProperties(void)
51 OSDictionary
*propTable
;
55 propTable
= OSDictionary::withCapacity(6);
59 prop
= OSData::withBytes((void *)(&_vendor
),strlen(_vendor
));
61 propTable
->setObject("vendor", prop
);
64 prop
= OSData::withBytes((void *)(&_product
),strlen(_product
));
66 propTable
->setObject("product", prop
);
69 prop
= OSData::withBytes((void *)(&_rev
),strlen(_rev
));
71 propTable
->setObject("revision", prop
);
74 typeString
= (char *)getDeviceTypeName();
75 prop
= OSData::withBytes((void *)(typeString
),strlen(typeString
));
77 propTable
->setObject("device-type", prop
);
81 prop
= OSData::withBytes((void *)(&_removable
),sizeof(bool));
83 propTable
->setObject("removable", prop
);
86 prop
= OSData::withBytes((void *)(&_ejectable
),sizeof(bool));
88 propTable
->setObject("ejectable", prop
);
98 IOSCSIHDDrive::createFormatCdb(UInt64
/* byteCapacity */,
99 UInt8
*cdb
,UInt32
*cdbLength
,
100 UInt8 buf
[],UInt32 bufLen
,
101 UInt32
*maxAutoSenseLength
,UInt32
*timeoutSeconds
)
103 struct IOFormatcdb
*c
;
104 UInt8 formatControls
; /* CmpLst & Defect List Format bits */
106 c
= (struct IOFormatcdb
*)cdb
;
108 c
->opcode
= kIOSCSICommandFormatUnit
;
111 c
->interleave_msb
= 0;
112 c
->interleave_lsb
= 0;
117 /* If we are to use a format buffer, set it up: */
120 formatControls
= composeFormatBuffer(buf
,bufLen
);
121 c
->lunbits
|= (formatControls
| 0x10); /* data transfer will occur */
124 *maxAutoSenseLength
= sizeof(SCSISenseData
); /* do the sense */
125 *timeoutSeconds
= 0; /* infinitely long time */
131 IOSCSIHDDrive::createNub(void)
135 // IOLog("%s[IOSCSIHDDrive]::createNub\n",getName());
137 /* Instantiate a nub so a generic driver can match above us. */
139 nub
= instantiateNub();
141 IOLog("%s[IOSCSIHDDrive]::createNub; nub didn't instantiate\n",getName());
147 if (!nub
->attach(this)) {
148 IOPanic("IOSCSIHDDrive::createNub; couldn't attach IOSCSIHDDriveNub");
151 nub
->registerService();
157 IOSCSIHDDrive::deleteFormatBuffer(UInt8
* /* buf */, UInt32
/* buflen */)
159 /* The default implementation has no buffer to free. */
163 IOSCSIHDDrive::deviceTypeMatches(UInt8 inqBuf
[],UInt32 inqLen
,SInt32
* /*score*/)
165 if ((_inqBuf
[0] & 0x1f) == kIOSCSIDeviceTypeDirectAccess
) {
168 return(false); /* we don't handle other devices */
173 IOSCSIHDDrive::doAsyncReadWrite(IOMemoryDescriptor
*buffer
,
174 UInt32 block
,UInt32 nblks
,
175 IOStorageCompletion completion
)
177 return(standardAsyncReadWrite(buffer
,block
,nblks
,completion
));
181 IOSCSIHDDrive::doEjectMedia(void)
183 /* Spin down, eject, and leave power alone: */
185 return(doStartStop(false,true,IOStartStopcdb::P_NOCHANGE
));
189 IOSCSIHDDrive::doFormatMedia(UInt64 byteCapacity
)
191 return(standardFormatMedia(byteCapacity
));
195 IOSCSIHDDrive::doGetFormatCapacities(UInt64
* capacities
,
196 UInt32 capacitiesMaxCount
) const
198 if ((capacities
!= NULL
) && (capacitiesMaxCount
> 0)) {
199 *capacities
= _blockSize
* (_maxBlock
+ 1);
206 /* We issue a simple Prevent/Allow command to lock or unlock the media: */
208 IOSCSIHDDrive::doLockUnlockMedia(bool doLock
)
211 struct IOPrevAllowcdb
*c
;
216 cx
= allocateContext();
218 return(kIOReturnNoMemory
);
223 bzero( &scsiCDB
, sizeof(scsiCDB
) );
225 c
= (struct IOPrevAllowcdb
*)&scsiCDB
.cdb
;
227 c
->opcode
= kIOSCSICommandPreventAllow
;
233 c
->prevent
= 0x01; /* prevent removal from device */
235 c
->prevent
= 0x00; /* allow removal from device */
240 scsiCDB
.cdbLength
= 6;
242 req
->setCDB( &scsiCDB
);
246 req
->setPointers( cx
->memory
, 0, false );
248 queueCommand(cx
,kSync
,getLockUnlockMediaPowerState()); /* queue the operation, sleep awaiting power */
250 result
= simpleSynchIO(cx
);
258 IOSCSIHDDrive::doStart(void)
260 return(doStartStop(true,false,IOStartStopcdb::P_ACTIVE
));
264 IOSCSIHDDrive::doStop(void)
266 return(doStartStop(false,false,IOStartStopcdb::P_NOCHANGE
));
270 IOSCSIHDDrive::doStartStop(bool start
,bool loadEject
,UInt8 powerCondition
)
273 struct IOStartStopcdb
*c
;
277 UInt32 powerLevel
; /* what power level we need to be in */
279 /* Issue a Start/Stop Unit command. */
281 cx
= allocateContext();
283 return(kIOReturnNoMemory
);
286 powerLevel
= getStopPowerState(); /* assume we're spinning down */
289 bzero( &scsiCDB
, sizeof(SCSICDBInfo
) );
291 c
= (struct IOStartStopcdb
*)&scsiCDB
.cdb
;
292 c
->opcode
= kIOSCSICommandStartStopUnit
;
296 c
->controls
= powerCondition
;
297 c
->controls
= 0; /* xxx powerCondition is a SCSI-3 thing */
299 c
->controls
|= IOStartStopcdb::C_LOEJ
;
300 powerLevel
= getEjectPowerState(); /* let subclass decide what we need */
303 c
->controls
|= IOStartStopcdb::C_SPINUP
;
304 powerLevel
= getStartPowerState();
308 scsiCDB
.cdbLength
= 6;
310 req
->setCDB( &scsiCDB
);
311 req
->setTimeout( 30000 );
315 req
->setPointers( cx
->memory
, 0, false );
317 queueCommand(cx
,kSync
,powerLevel
); /* queue the operation, sleep awaiting power */
319 result
= simpleSynchIO(cx
);
326 IOSCSIHDDrive::doSynchronizeCache(void)
328 return(standardSynchronizeCache());
332 IOSCSIHDDrive::doSyncReadWrite(IOMemoryDescriptor
*buffer
,UInt32 block
,UInt32 nblks
)
334 return(standardSyncReadWrite(buffer
,block
,nblks
));
338 IOSCSIHDDrive::getDeviceTypeName(void)
340 return(kIOBlockStorageDeviceTypeGeneric
);
344 IOSCSIHDDrive::getEjectPowerState(void)
346 return(kElectronicsOn
);
350 IOSCSIHDDrive::getExecuteCDBPowerState(void)
356 IOSCSIHDDrive::getFormatMediaPowerState(void)
362 IOSCSIHDDrive::getInitialPowerState(void)
368 IOSCSIHDDrive::getInquiryPowerState(void)
370 return(kElectronicsOn
);
374 IOSCSIHDDrive::getLockUnlockMediaPowerState(void)
376 return(kElectronicsOn
);
380 IOSCSIHDDrive::getReadCapacityPowerState(void)
382 return(kElectronicsOn
);
386 IOSCSIHDDrive::getReadWritePowerState(void)
392 IOSCSIHDDrive::getReportWriteProtectionPowerState(void)
394 return(kElectronicsOn
);
398 IOSCSIHDDrive::getStartPowerState(void)
400 return(kElectronicsOn
);
404 IOSCSIHDDrive::getStopPowerState(void)
406 return(kElectronicsOn
); /* we don't have to be spinning to spin down */
410 IOSCSIHDDrive::getSynchronizeCachePowerState(void)
416 IOSCSIHDDrive::getTestUnitReadyPowerState(void)
418 return(kElectronicsOn
);
422 IOSCSIHDDrive::init(OSDictionary
* properties
)
424 _mediaPresent
= false;
425 _startStopDisabled
= false;
427 return(super::init(properties
));
431 IOSCSIHDDrive::instantiateNub(void)
435 /* Instantiate a nub so a generic driver can match above us. */
437 nub
= new IOSCSIHDDriveNub
;
442 IOSCSIHDDrive::powerTickle(UInt32 desiredState
)
444 return(activityTickle(kIOPMSuperclassPolicy1
,desiredState
));
448 IOSCSIHDDrive::reportMediaState(bool *mediaPresent
,bool *changed
)
454 SCSIResults scsiResults
;
459 cx
= allocateContext();
461 return(kIOReturnNoMemory
);
466 bzero( &scsiCDB
, sizeof(scsiCDB
) );
468 c
= (struct IOTURcdb
*)&scsiCDB
.cdb
;
469 c
->opcode
= kIOSCSICommandTestUnitReady
;
476 scsiCDB
.cdbLength
= 6;
478 req
->setCDB( &scsiCDB
);
479 req
->setPointers( cx
->senseDataDesc
, 255, false, true );
481 req
->setTimeout( 5000 );
485 req
->setPointers( cx
->memory
, 0, false );
488 IOLog("IOSCSIHDDrive::reportMediaState: mp=%08x,ch=%08x\n",
489 (int)mediaPresent,(int)changed);
490 IOLog("IOSCSIHDDrive::reportMediaState: doing TUR\n");
493 queueCommand(cx
,kSync
,getTestUnitReadyPowerState());
494 result
= simpleSynchIO(cx
);
496 req
->getResults( &scsiResults
);
498 status
= scsiResults
.scsiStatus
;
501 IOLog("%s[IOSCSIHDDrive]::reportMediaState; result=%s, status=%02x,sense=%02x\n",
502 getName(),stringFromReturn(result),status,cx->senseData->senseKey
506 if (result
== kIOReturnSuccess
) { /* TUR succeeded; device is ready */
508 *mediaPresent
= true;
509 *changed
= (*mediaPresent
!= _mediaPresent
); /* report if it's changed */
510 _mediaPresent
= true; /* remember current state */
511 result
= kIOReturnSuccess
;
513 } else { /* TUR failed; check sense key */
515 if ( scsiResults
.requestSenseDone
== true ) {
516 senseKey
= cx
->senseData
->senseKey
;
518 if (senseKey
== 0x02) { /* device says "not ready" */
519 *mediaPresent
= false;
520 *changed
= (*mediaPresent
!= _mediaPresent
); /* report if it's changed */
521 _mediaPresent
= false; /* remember current state */
522 result
= kIOReturnSuccess
;
524 } else { /* funky sense key? forget it. */
526 *mediaPresent
= false;
527 *changed
= (*mediaPresent
!= _mediaPresent
); /* report if it's changed */
528 _mediaPresent
= false; /* remember current state */
529 result
= kIOReturnIOError
;
531 IOLog("%s[IOSCSIHDDrive]:: reportMediaState; funky sense key %d\n",
535 } else { /* autosense not done! */
537 /* This condition has been observed with the Matsushita PD-2 DVD-RAM on the
538 * Curio (external) bus on an 8500. I can't figure out why we get a good status
539 * but no autosense (after going through Unit-Attention.) We ignore the current
540 * media check and it'll operate normally on the next pass through.
543 IOLog("%s[IOSCSIHDDrive]:: reportMediaState; autosense not done: ",getName());
544 IOLog("result = '%s', status = %d, senseKey = %d\n",
545 stringFromReturn(result),status,cx->senseData->senseKey);
547 *mediaPresent
= _mediaPresent
;
549 result
= kIOReturnSuccess
;
553 if (*changed
&& *mediaPresent
) {
554 _readCapDone
= false;
562 if (*changed
&& *mediaPresent
)
567 if (result != kIOReturnSuccess) {
568 IOLog("%s[IOSCSIHDDrive]:: reportMediaState; returning %d %x '%s'\n",
569 getName(),result,result,stringFromReturn(result));
576 IOSCSIHDDrive::restoreElectronicsState(void)
578 return(kIOReturnSuccess
);
581 /* The standard completion for a doAsyncReadWrite operation. We fire it
582 * up to our target, the generic driver.
585 IOSCSIHDDrive::RWCompletion(struct context
*cx
)
587 SCSIResults scsiResults
;
589 cx
->scsireq
->getResults( &scsiResults
);
591 IOStorage::complete(cx
->completion
,
592 scsiResults
.returnCode
,
593 scsiResults
.bytesTransferred
);
596 /* Attempt to dequeue and execute any waiting commands: */
602 IOSCSIHDDrive::saveElectronicsState(void)
604 return(kIOReturnSuccess
);
607 static IOPMPowerState ourPowerStates
[kNumberOfPowerStates
] = {
608 {1,IOPMNotAttainable
,0,0,0,0,0,0,0,0,0,0}, /* state 00 kAllOff */
609 {1,0,0,IOPMPowerOn
,0,0,0,0,0,0,0,0}, /* state 01 kElectronicsOn */
610 {1,0,0,IOPMPowerOn
,0,0,0,0,0,0,0,0} /* state 02 kAllOn */
614 IOSCSIHDDrive::standardFormatMedia(UInt64 byteCapacity
)
621 UInt32 transferLength
;
623 UInt32 timeoutSeconds
;
625 cx
= allocateContext();
627 return(kIOReturnNoMemory
);
632 /* Allow a subclass to construct the cdb and return an optional
633 * memory buffer address for defect lists, etc.
636 result
= allocateFormatBuffer(&fmtbuf
,&transferLength
);
637 if (result
!= kIOReturnSuccess
) {
641 bzero( &scsiCDB
, sizeof(scsiCDB
) );
643 scsiCDB
.cdbFlags
|= createFormatCdb(byteCapacity
,(UInt8
*)&scsiCDB
.cdb
,&scsiCDB
.cdbLength
,
644 fmtbuf
,transferLength
,
648 req
->setCDB( &scsiCDB
);
649 req
->setPointers( cx
->senseDataDesc
, senseLength
, false, true );
650 req
->setTimeout( timeoutSeconds
* 1000 );
652 /* If we have a buffer to transfer, create a Memory Descriptor for it: */
654 if ((fmtbuf
!= NULL
) && (transferLength
!= 0)) {
655 cx
->memory
= IOMemoryDescriptor::withAddress((void *)fmtbuf
,
660 req
->setPointers( cx
->memory
, transferLength
, true );
661 queueCommand(cx
,kSync
,getFormatMediaPowerState()); /* queue the operation, sleep awaiting power */
663 result
= simpleSynchIO(cx
); /* issue a simple command */
665 /* Free the format buffer, if any: */
667 deleteFormatBuffer(fmtbuf
,transferLength
);
675 IOSCSIHDDrive::standardSynchronizeCache(void)
678 struct IOSyncCachecdb
*c
;
683 cx
= allocateContext();
685 return(kIOReturnNoMemory
);
689 bzero( &scsiCDB
, sizeof(scsiCDB
) );
691 c
= (struct IOSyncCachecdb
*)&scsiCDB
.cdb
;
693 c
->opcode
= kIOSCSICommandSynchronizeCache
;
695 c
->lba_3
= 0; /* if zero, start at block zero */
700 c
->nblks_msb
= 0; /* if zero, do all blocks */
704 scsiCDB
.cdbLength
= 10;
706 req
->setCDB( &scsiCDB
);
710 req
->setPointers( cx
->memory
, 0, false );
712 /* We assume there will be some data in the drive's cache, so we force the
713 * drive to be running before we issue this command.
716 queueCommand(cx
,kSync
,getSynchronizeCachePowerState()); /* queue the operation, sleep awaiting power */
718 result
= simpleSynchIO(cx
);
726 IOSCSIHDDrive::start(IOService
*provider
)
730 if (!super::start(provider
)) {
734 // IOLog("%s[IOSCSIHDDrive]::start\n",getName());
736 /* Initialize and set up to perform Power Management: */
739 _restoreState
= false;
740 #ifdef notyet // don't register for PM yet till we handle queuing requests!
741 IOPMRegisterDevice(pm_vars
->ourName
,this); // join the power management tree
743 registerPowerDriver(this,ourPowerStates
,kNumberOfPowerStates
); // export power states
753 // **********************************************************************************
754 // maxCapabilityForDomainState
756 // This simple device needs only power. If the power domain is supplying
757 // power, the disk can go to its highest state. If there is no power
758 // it can only be in its lowest state, which is off.
759 // **********************************************************************************
762 IOSCSIHDDrive::maxCapabilityForDomainState(IOPMPowerFlags domainState
)
764 if (domainState
& IOPMPowerOn
) {
771 // **********************************************************************************
772 // powerStateForDomainState
774 // The power domain may be changing state. If power is ON in its new
775 // state, we will be on, too. If domain power is OFF, we are off.
776 // **********************************************************************************
778 IOSCSIHDDrive::powerStateForDomainState(IOPMPowerFlags domainState
)
780 if (domainState
& IOPMPowerOn
) {
781 return(kAllOn
); /* xxx might be kElectronicsOn if drive not spun up */
787 // **********************************************************************************
788 // initialPowerStateForDomainState
790 // Our parent wants to know what our initial power state is. If power is ON in the
791 // domain, we are in state kElectronicsOn or kAllOn. If domain power is OFF, we are off.
792 // **********************************************************************************
794 IOSCSIHDDrive::initialPowerStateForDomainState(IOPMPowerFlags domainState
)
796 if (domainState
& IOPMPowerOn
) {
797 return(getInitialPowerState()); /* report whether it's spinning on startup */
803 // **********************************************************************************
806 // Someone has decided to change the disk state. We perform the change here.
807 // **********************************************************************************
809 IOSCSIHDDrive::setPowerState(unsigned long powerStateOrdinal
,IOService
*)
813 result
= kIOReturnSuccess
;
815 /* All we do in the default implementation is spin up and down. If the drive reports an
816 * error to a start/stop command, we don't bother attempting to issue those commands again.
818 * xxx Question: What should we return? Success? or an error meaning "we didn't do it!"
820 switch (powerStateOrdinal
) {
822 case kElectronicsOn
: /* spin down if necessary */
823 if (pm_vars
->myCurrentState
== kAllOn
) {
824 if (!_startStopDisabled
) {
826 if (result
!= kIOReturnSuccess
) {
827 _startStopDisabled
= true;
828 result
= kIOReturnSuccess
;
834 case kAllOn
: /* spin up if necessary */
835 if (pm_vars
->myCurrentState
== kElectronicsOn
) {
836 if (!_startStopDisabled
) {
838 if (result
!= kIOReturnSuccess
) {
839 _startStopDisabled
= true;
840 result
= kIOReturnSuccess
;
846 default: /* we don't do other states */
847 result
= kIOReturnSuccess
;
855 // **********************************************************************************
856 /* We get called here as an advisory that the power state will change. If we are coming up
857 * from the all-off state, remember to restore the electronics state when we later power up.
858 * If we are powering-down the electronics, save any required state now.
861 IOSCSIHDDrive::powerStateWillChangeTo(unsigned long,unsigned long stateOrdinal
,IOService
*)
863 if ((pm_vars
->myCurrentState
== kAllOff
) &&
864 (stateOrdinal
> kAllOff
)) { /* we're powering up from all-off */
865 _restoreState
= true;
868 if ((stateOrdinal
== kAllOff
) &&
869 (pm_vars
->myCurrentState
> kAllOff
)) { /* we're powering down to all-off */
870 saveElectronicsState();
873 return(IOPMAckImplied
);
876 // **********************************************************************************
877 /* We get called here when power has successfully changed state. */
879 IOSCSIHDDrive::powerStateDidChangeTo(unsigned long,unsigned long stateOrdinal
,IOService
*)
883 /* If we must restore the electronics state, do it now. */
886 result
= restoreElectronicsState();
887 _restoreState
= false;
890 /* If we have powered up into a state that can execute commands, release any queued
891 * requests that were awaiting the power change.
894 if (stateOrdinal
> kAllOff
) {
898 return IOPMAckImplied
;