]> git.saurik.com Git - apple/xnu.git/blob - iokit/Families/IOSCSIHDDrive/IOSCSIHDDrive.cpp
c74ec25e888e57fd62b62c268c43b961b838040d
[apple/xnu.git] / iokit / Families / IOSCSIHDDrive / IOSCSIHDDrive.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
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>
28
29 #define super IOBasicSCSI
30 OSDefineMetaClassAndStructors(IOSCSIHDDrive,IOBasicSCSI)
31
32 IOReturn
33 IOSCSIHDDrive::allocateFormatBuffer(UInt8 **buf,UInt32 *len)
34 {
35 /* The default implementation uses no buffer. */
36
37 *buf = 0;
38 *len = 0;
39 return(kIOReturnSuccess);
40 }
41
42 UInt8
43 IOSCSIHDDrive::composeFormatBuffer(UInt8 * /* buf */,UInt32 /* buflen */)
44 {
45 return(0); /* default: no fmtdata buffer to transfer */
46 }
47
48 OSDictionary *
49 IOSCSIHDDrive::constructDeviceProperties(void)
50 {
51 OSDictionary *propTable;
52 OSData *prop;
53 char *typeString;
54
55 propTable = OSDictionary::withCapacity(6);
56
57 if (propTable) {
58
59 prop = OSData::withBytes((void *)(&_vendor),strlen(_vendor));
60 if (prop) {
61 propTable->setObject("vendor", prop);
62 }
63
64 prop = OSData::withBytes((void *)(&_product),strlen(_product));
65 if (prop) {
66 propTable->setObject("product", prop);
67 }
68
69 prop = OSData::withBytes((void *)(&_rev),strlen(_rev));
70 if (prop) {
71 propTable->setObject("revision", prop);
72 }
73
74 typeString = (char *)getDeviceTypeName();
75 prop = OSData::withBytes((void *)(typeString),strlen(typeString));
76 if (prop) {
77 propTable->setObject("device-type", prop);
78 }
79
80 #ifdef xxx
81 prop = OSData::withBytes((void *)(&_removable),sizeof(bool));
82 if (prop) {
83 propTable->setObject("removable", prop);
84 }
85
86 prop = OSData::withBytes((void *)(&_ejectable),sizeof(bool));
87 if (prop) {
88 propTable->setObject("ejectable", prop);
89 }
90 #endif //xxx
91
92 }
93
94 return(propTable);
95 }
96
97 UInt32
98 IOSCSIHDDrive::createFormatCdb(UInt64 /* byteCapacity */,
99 UInt8 *cdb,UInt32 *cdbLength,
100 UInt8 buf[],UInt32 bufLen,
101 UInt32 *maxAutoSenseLength,UInt32 *timeoutSeconds)
102 {
103 struct IOFormatcdb *c;
104 UInt8 formatControls; /* CmpLst & Defect List Format bits */
105
106 c = (struct IOFormatcdb *)cdb;
107
108 c->opcode = kIOSCSICommandFormatUnit;
109 c->lunbits = 0;
110 c->vendor = 0;
111 c->interleave_msb = 0;
112 c->interleave_lsb = 0;
113 c->ctlbyte = 0;
114
115 *cdbLength = 6;
116
117 /* If we are to use a format buffer, set it up: */
118
119 if (buf != NULL) {
120 formatControls = composeFormatBuffer(buf,bufLen);
121 c->lunbits |= (formatControls | 0x10); /* data transfer will occur */
122 }
123
124 *maxAutoSenseLength = sizeof(SCSISenseData); /* do the sense */
125 *timeoutSeconds = 0; /* infinitely long time */
126
127 return(0);
128 }
129
130 IOService *
131 IOSCSIHDDrive::createNub(void)
132 {
133 IOService *nub;
134
135 // IOLog("%s[IOSCSIHDDrive]::createNub\n",getName());
136
137 /* Instantiate a nub so a generic driver can match above us. */
138
139 nub = instantiateNub();
140 if (nub == NULL) {
141 IOLog("%s[IOSCSIHDDrive]::createNub; nub didn't instantiate\n",getName());
142 return(NULL);
143 }
144
145 nub->init();
146
147 if (!nub->attach(this)) {
148 IOPanic("IOSCSIHDDrive::createNub; couldn't attach IOSCSIHDDriveNub");
149 }
150
151 nub->registerService();
152
153 return(nub);
154 }
155
156 void
157 IOSCSIHDDrive::deleteFormatBuffer(UInt8 * /* buf */, UInt32 /* buflen */)
158 {
159 /* The default implementation has no buffer to free. */
160 }
161
162 bool
163 IOSCSIHDDrive::deviceTypeMatches(UInt8 inqBuf[],UInt32 inqLen,SInt32 * /*score*/)
164 {
165 if ((_inqBuf[0] & 0x1f) == kIOSCSIDeviceTypeDirectAccess) {
166 return(true);
167 } else {
168 return(false); /* we don't handle other devices */
169 }
170 }
171
172 IOReturn
173 IOSCSIHDDrive::doAsyncReadWrite(IOMemoryDescriptor *buffer,
174 UInt32 block,UInt32 nblks,
175 IOStorageCompletion completion)
176 {
177 return(standardAsyncReadWrite(buffer,block,nblks,completion));
178 }
179
180 IOReturn
181 IOSCSIHDDrive::doEjectMedia(void)
182 {
183 /* Spin down, eject, and leave power alone: */
184
185 return(doStartStop(false,true,IOStartStopcdb::P_NOCHANGE));
186 }
187
188 IOReturn
189 IOSCSIHDDrive::doFormatMedia(UInt64 byteCapacity)
190 {
191 return(standardFormatMedia(byteCapacity));
192 }
193
194 UInt32
195 IOSCSIHDDrive::doGetFormatCapacities(UInt64 * capacities,
196 UInt32 capacitiesMaxCount) const
197 {
198 if ((capacities != NULL) && (capacitiesMaxCount > 0)) {
199 *capacities = _blockSize * (_maxBlock + 1);
200 return(1);
201 } else {
202 return(0);
203 }
204 }
205
206 /* We issue a simple Prevent/Allow command to lock or unlock the media: */
207 IOReturn
208 IOSCSIHDDrive::doLockUnlockMedia(bool doLock)
209 {
210 struct context *cx;
211 struct IOPrevAllowcdb *c;
212 IOSCSICommand *req;
213 SCSICDBInfo scsiCDB;
214 IOReturn result;
215
216 cx = allocateContext();
217 if (cx == NULL) {
218 return(kIOReturnNoMemory);
219 }
220
221 req = cx->scsireq;
222
223 bzero( &scsiCDB, sizeof(scsiCDB) );
224
225 c = (struct IOPrevAllowcdb *)&scsiCDB.cdb;
226
227 c->opcode = kIOSCSICommandPreventAllow;
228 c->lunbits = 0;
229 c->reserved1 = 0;
230 c->reserved2 = 0;
231
232 if (doLock) {
233 c->prevent = 0x01; /* prevent removal from device */
234 } else {
235 c->prevent = 0x00; /* allow removal from device */
236 }
237
238 c->ctlbyte = 0;
239
240 scsiCDB.cdbLength = 6;
241
242 req->setCDB( &scsiCDB );
243
244 cx->memory = 0;
245
246 req->setPointers( cx->memory, 0, false );
247
248 queueCommand(cx,kSync,getLockUnlockMediaPowerState()); /* queue the operation, sleep awaiting power */
249
250 result = simpleSynchIO(cx);
251
252 deleteContext(cx);
253
254 return(result);
255 }
256
257 IOReturn
258 IOSCSIHDDrive::doStart(void)
259 {
260 return(doStartStop(true,false,IOStartStopcdb::P_ACTIVE));
261 }
262
263 IOReturn
264 IOSCSIHDDrive::doStop(void)
265 {
266 return(doStartStop(false,false,IOStartStopcdb::P_NOCHANGE));
267 }
268
269 IOReturn
270 IOSCSIHDDrive::doStartStop(bool start,bool loadEject,UInt8 powerCondition)
271 {
272 struct context *cx;
273 struct IOStartStopcdb *c;
274 IOSCSICommand *req;
275 SCSICDBInfo scsiCDB;
276 IOReturn result;
277 UInt32 powerLevel; /* what power level we need to be in */
278
279 /* Issue a Start/Stop Unit command. */
280
281 cx = allocateContext();
282 if (cx == NULL) {
283 return(kIOReturnNoMemory);
284 }
285
286 powerLevel = getStopPowerState(); /* assume we're spinning down */
287 req = cx->scsireq;
288
289 bzero( &scsiCDB, sizeof(SCSICDBInfo) );
290
291 c = (struct IOStartStopcdb *)&scsiCDB.cdb;
292 c->opcode = kIOSCSICommandStartStopUnit;
293 c->lunImmed = 0;
294 c->reserved1 = 0;
295 c->reserved2 = 0;
296 c->controls = powerCondition;
297 c->controls = 0; /* xxx powerCondition is a SCSI-3 thing */
298 if (loadEject) {
299 c->controls |= IOStartStopcdb::C_LOEJ;
300 powerLevel = getEjectPowerState(); /* let subclass decide what we need */
301 };
302 if (start) {
303 c->controls |= IOStartStopcdb::C_SPINUP;
304 powerLevel = getStartPowerState();
305 }
306 c->ctlbyte = 0;
307
308 scsiCDB.cdbLength = 6;
309
310 req->setCDB( &scsiCDB );
311 req->setTimeout( 30000 );
312
313 cx->memory = 0;
314
315 req->setPointers( cx->memory, 0, false );
316
317 queueCommand(cx,kSync,powerLevel); /* queue the operation, sleep awaiting power */
318
319 result = simpleSynchIO(cx);
320
321 deleteContext(cx);
322 return(result);
323 }
324
325 IOReturn
326 IOSCSIHDDrive::doSynchronizeCache(void)
327 {
328 return(standardSynchronizeCache());
329 }
330
331 IOReturn
332 IOSCSIHDDrive::doSyncReadWrite(IOMemoryDescriptor *buffer,UInt32 block,UInt32 nblks)
333 {
334 return(standardSyncReadWrite(buffer,block,nblks));
335 }
336
337 const char *
338 IOSCSIHDDrive::getDeviceTypeName(void)
339 {
340 return(kIOBlockStorageDeviceTypeGeneric);
341 }
342
343 UInt32
344 IOSCSIHDDrive::getEjectPowerState(void)
345 {
346 return(kElectronicsOn);
347 }
348
349 UInt32
350 IOSCSIHDDrive::getExecuteCDBPowerState(void)
351 {
352 return(kAllOn);
353 }
354
355 UInt32
356 IOSCSIHDDrive::getFormatMediaPowerState(void)
357 {
358 return(kAllOn);
359 }
360
361 UInt32
362 IOSCSIHDDrive::getInitialPowerState(void)
363 {
364 return(kAllOn);
365 }
366
367 UInt32
368 IOSCSIHDDrive::getInquiryPowerState(void)
369 {
370 return(kElectronicsOn);
371 }
372
373 UInt32
374 IOSCSIHDDrive::getLockUnlockMediaPowerState(void)
375 {
376 return(kElectronicsOn);
377 }
378
379 UInt32
380 IOSCSIHDDrive::getReadCapacityPowerState(void)
381 {
382 return(kElectronicsOn);
383 }
384
385 UInt32
386 IOSCSIHDDrive::getReadWritePowerState(void)
387 {
388 return(kAllOn);
389 }
390
391 UInt32
392 IOSCSIHDDrive::getReportWriteProtectionPowerState(void)
393 {
394 return(kElectronicsOn);
395 }
396
397 UInt32
398 IOSCSIHDDrive::getStartPowerState(void)
399 {
400 return(kElectronicsOn);
401 }
402
403 UInt32
404 IOSCSIHDDrive::getStopPowerState(void)
405 {
406 return(kElectronicsOn); /* we don't have to be spinning to spin down */
407 }
408
409 UInt32
410 IOSCSIHDDrive::getSynchronizeCachePowerState(void)
411 {
412 return(kAllOn);
413 }
414
415 UInt32
416 IOSCSIHDDrive::getTestUnitReadyPowerState(void)
417 {
418 return(kElectronicsOn);
419 }
420
421 bool
422 IOSCSIHDDrive::init(OSDictionary * properties)
423 {
424 _mediaPresent = false;
425 _startStopDisabled = false;
426
427 return(super::init(properties));
428 }
429
430 IOService *
431 IOSCSIHDDrive::instantiateNub(void)
432 {
433 IOService *nub;
434
435 /* Instantiate a nub so a generic driver can match above us. */
436
437 nub = new IOSCSIHDDriveNub;
438 return(nub);
439 }
440
441 bool
442 IOSCSIHDDrive::powerTickle(UInt32 desiredState)
443 {
444 return(activityTickle(kIOPMSuperclassPolicy1,desiredState));
445 }
446
447 IOReturn
448 IOSCSIHDDrive::reportMediaState(bool *mediaPresent,bool *changed)
449 {
450 struct context *cx;
451 struct IOTURcdb *c;
452 IOSCSICommand *req;
453 SCSICDBInfo scsiCDB;
454 SCSIResults scsiResults;
455 IOReturn result;
456 UInt8 status;
457 UInt8 senseKey;
458
459 cx = allocateContext();
460 if (cx == NULL) {
461 return(kIOReturnNoMemory);
462 }
463
464 req = cx->scsireq;
465
466 bzero( &scsiCDB, sizeof(scsiCDB) );
467
468 c = (struct IOTURcdb *)&scsiCDB.cdb;
469 c->opcode = kIOSCSICommandTestUnitReady;
470 c->lunbits = 0;
471 c->reserved1 = 0;
472 c->reserved2 = 0;
473 c->reserved3 = 0;
474 c->ctlbyte = 0;
475
476 scsiCDB.cdbLength = 6;
477
478 req->setCDB( &scsiCDB );
479 req->setPointers( cx->senseDataDesc, 255, false, true );
480
481 req->setTimeout( 5000 );
482
483 cx->memory = 0;
484
485 req->setPointers( cx->memory, 0, false );
486
487 /**
488 IOLog("IOSCSIHDDrive::reportMediaState: mp=%08x,ch=%08x\n",
489 (int)mediaPresent,(int)changed);
490 IOLog("IOSCSIHDDrive::reportMediaState: doing TUR\n");
491 **/
492
493 queueCommand(cx,kSync,getTestUnitReadyPowerState());
494 result = simpleSynchIO(cx);
495
496 req->getResults( &scsiResults );
497
498 status = scsiResults.scsiStatus;
499
500 /**
501 IOLog("%s[IOSCSIHDDrive]::reportMediaState; result=%s, status=%02x,sense=%02x\n",
502 getName(),stringFromReturn(result),status,cx->senseData->senseKey
503 );
504 **/
505
506 if (result == kIOReturnSuccess) { /* TUR succeeded; device is ready */
507
508 *mediaPresent = true;
509 *changed = (*mediaPresent != _mediaPresent); /* report if it's changed */
510 _mediaPresent = true; /* remember current state */
511 result = kIOReturnSuccess;
512
513 } else { /* TUR failed; check sense key */
514
515 if ( scsiResults.requestSenseDone == true ) {
516 senseKey = cx->senseData->senseKey;
517
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;
523
524 } else { /* funky sense key? forget it. */
525
526 *mediaPresent = false;
527 *changed = (*mediaPresent != _mediaPresent); /* report if it's changed */
528 _mediaPresent = false; /* remember current state */
529 result = kIOReturnIOError;
530 /**
531 IOLog("%s[IOSCSIHDDrive]:: reportMediaState; funky sense key %d\n",
532 getName(),senseKey);
533 **/
534 }
535 } else { /* autosense not done! */
536
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.
541 */
542 /**
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);
546 **/
547 *mediaPresent = _mediaPresent;
548 *changed = false;
549 result = kIOReturnSuccess;
550 }
551 }
552
553 if (*changed && *mediaPresent) {
554 _readCapDone = false;
555 _blockSize = 0;
556 _maxBlock = 0;
557 }
558
559 deleteContext(cx);
560
561 #ifndef DISKPM
562 if (*changed && *mediaPresent)
563 doStart();
564 #endif
565
566 /**
567 if (result != kIOReturnSuccess) {
568 IOLog("%s[IOSCSIHDDrive]:: reportMediaState; returning %d %x '%s'\n",
569 getName(),result,result,stringFromReturn(result));
570 }
571 **/
572 return(result);
573 }
574
575 IOReturn
576 IOSCSIHDDrive::restoreElectronicsState(void)
577 {
578 return(kIOReturnSuccess);
579 }
580
581 /* The standard completion for a doAsyncReadWrite operation. We fire it
582 * up to our target, the generic driver.
583 */
584 void
585 IOSCSIHDDrive::RWCompletion(struct context *cx)
586 {
587 SCSIResults scsiResults;
588
589 cx->scsireq->getResults( &scsiResults );
590
591 IOStorage::complete(cx->completion,
592 scsiResults.returnCode,
593 scsiResults.bytesTransferred);
594
595
596 /* Attempt to dequeue and execute any waiting commands: */
597
598 dequeueCommands();
599 }
600
601 IOReturn
602 IOSCSIHDDrive::saveElectronicsState(void)
603 {
604 return(kIOReturnSuccess);
605 }
606
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 */
611 };
612
613 IOReturn
614 IOSCSIHDDrive::standardFormatMedia(UInt64 byteCapacity)
615 {
616 struct context *cx;
617 UInt8 *fmtbuf;
618 IOReturn result;
619 IOSCSICommand *req;
620 SCSICDBInfo scsiCDB;
621 UInt32 transferLength;
622 UInt32 senseLength;
623 UInt32 timeoutSeconds;
624
625 cx = allocateContext();
626 if (cx == NULL) {
627 return(kIOReturnNoMemory);
628 }
629
630 req = cx->scsireq;
631
632 /* Allow a subclass to construct the cdb and return an optional
633 * memory buffer address for defect lists, etc.
634 */
635
636 result = allocateFormatBuffer(&fmtbuf,&transferLength);
637 if (result != kIOReturnSuccess) {
638 return(result);
639 }
640
641 bzero( &scsiCDB, sizeof(scsiCDB) );
642
643 scsiCDB.cdbFlags |= createFormatCdb(byteCapacity,(UInt8 *)&scsiCDB.cdb,&scsiCDB.cdbLength,
644 fmtbuf,transferLength,
645 &senseLength,
646 &timeoutSeconds);
647
648 req->setCDB( &scsiCDB );
649 req->setPointers( cx->senseDataDesc, senseLength, false, true );
650 req->setTimeout( timeoutSeconds * 1000 );
651
652 /* If we have a buffer to transfer, create a Memory Descriptor for it: */
653
654 if ((fmtbuf != NULL) && (transferLength != 0)) {
655 cx->memory = IOMemoryDescriptor::withAddress((void *)fmtbuf,
656 transferLength,
657 kIODirectionOut);
658 }
659
660 req->setPointers( cx->memory, transferLength, true );
661 queueCommand(cx,kSync,getFormatMediaPowerState()); /* queue the operation, sleep awaiting power */
662
663 result = simpleSynchIO(cx); /* issue a simple command */
664
665 /* Free the format buffer, if any: */
666
667 deleteFormatBuffer(fmtbuf,transferLength);
668
669 deleteContext(cx);
670
671 return(result);
672 }
673
674 IOReturn
675 IOSCSIHDDrive::standardSynchronizeCache(void)
676 {
677 struct context *cx;
678 struct IOSyncCachecdb *c;
679 IOSCSICommand *req;
680 SCSICDBInfo scsiCDB;
681 IOReturn result;
682
683 cx = allocateContext();
684 if (cx == NULL) {
685 return(kIOReturnNoMemory);
686 }
687
688 req = cx->scsireq;
689 bzero( &scsiCDB, sizeof(scsiCDB) );
690
691 c = (struct IOSyncCachecdb *)&scsiCDB.cdb;
692
693 c->opcode = kIOSCSICommandSynchronizeCache;
694 c->lunbits = 0;
695 c->lba_3 = 0; /* if zero, start at block zero */
696 c->lba_2 = 0;
697 c->lba_1 = 0;
698 c->lba_0 = 0;
699 c->reserved = 0;
700 c->nblks_msb = 0; /* if zero, do all blocks */
701 c->nblks_lsb = 0;
702 c->ctlbyte = 0;
703
704 scsiCDB.cdbLength = 10;
705
706 req->setCDB( &scsiCDB );
707
708 cx->memory = 0;
709
710 req->setPointers( cx->memory, 0, false );
711
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.
714 */
715
716 queueCommand(cx,kSync,getSynchronizeCachePowerState()); /* queue the operation, sleep awaiting power */
717
718 result = simpleSynchIO(cx);
719
720 deleteContext(cx);
721
722 return(result);
723 }
724
725 bool
726 IOSCSIHDDrive::start(IOService *provider)
727 {
728 IOService *nub;
729
730 if (!super::start(provider)) {
731 return(false);
732 }
733
734 // IOLog("%s[IOSCSIHDDrive]::start\n",getName());
735
736 /* Initialize and set up to perform Power Management: */
737
738 PMinit();
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
742 #endif
743 registerPowerDriver(this,ourPowerStates,kNumberOfPowerStates); // export power states
744
745 nub = createNub();
746 if (nub == NULL) {
747 return(false);
748 } else {
749 return(true);
750 }
751 }
752
753 // **********************************************************************************
754 // maxCapabilityForDomainState
755 //
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 // **********************************************************************************
760
761 unsigned long
762 IOSCSIHDDrive::maxCapabilityForDomainState(IOPMPowerFlags domainState)
763 {
764 if (domainState & IOPMPowerOn) {
765 return(kAllOn);
766 } else {
767 return(kAllOff);
768 }
769 }
770
771 // **********************************************************************************
772 // powerStateForDomainState
773 //
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 // **********************************************************************************
777 unsigned long
778 IOSCSIHDDrive::powerStateForDomainState(IOPMPowerFlags domainState)
779 {
780 if (domainState & IOPMPowerOn) {
781 return(kAllOn); /* xxx might be kElectronicsOn if drive not spun up */
782 } else {
783 return(kAllOff);
784 }
785 }
786
787 // **********************************************************************************
788 // initialPowerStateForDomainState
789 //
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 // **********************************************************************************
793 unsigned long
794 IOSCSIHDDrive::initialPowerStateForDomainState(IOPMPowerFlags domainState)
795 {
796 if (domainState & IOPMPowerOn) {
797 return(getInitialPowerState()); /* report whether it's spinning on startup */
798 } else {
799 return(kAllOff);
800 }
801 }
802
803 // **********************************************************************************
804 // setPowerState
805 //
806 // Someone has decided to change the disk state. We perform the change here.
807 // **********************************************************************************
808 IOReturn
809 IOSCSIHDDrive::setPowerState(unsigned long powerStateOrdinal,IOService *)
810 {
811 IOReturn result;
812
813 result = kIOReturnSuccess;
814
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.
817 *
818 * xxx Question: What should we return? Success? or an error meaning "we didn't do it!"
819 */
820 switch (powerStateOrdinal) {
821
822 case kElectronicsOn : /* spin down if necessary */
823 if (pm_vars->myCurrentState == kAllOn) {
824 if (!_startStopDisabled) {
825 result = doStop();
826 if (result != kIOReturnSuccess) {
827 _startStopDisabled = true;
828 result = kIOReturnSuccess;
829 }
830 }
831 }
832 break;
833
834 case kAllOn : /* spin up if necessary */
835 if (pm_vars->myCurrentState == kElectronicsOn) {
836 if (!_startStopDisabled) {
837 result = doStart();
838 if (result != kIOReturnSuccess) {
839 _startStopDisabled = true;
840 result = kIOReturnSuccess;
841 }
842 }
843 }
844 break;
845
846 default: /* we don't do other states */
847 result = kIOReturnSuccess;
848 break;
849
850 }
851
852 return(result);
853 }
854
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.
859 */
860 IOReturn
861 IOSCSIHDDrive::powerStateWillChangeTo(unsigned long,unsigned long stateOrdinal,IOService *)
862 {
863 if ((pm_vars->myCurrentState == kAllOff) &&
864 (stateOrdinal > kAllOff)) { /* we're powering up from all-off */
865 _restoreState = true;
866 }
867
868 if ((stateOrdinal == kAllOff) &&
869 (pm_vars->myCurrentState > kAllOff)) { /* we're powering down to all-off */
870 saveElectronicsState();
871 }
872
873 return(IOPMAckImplied);
874 }
875
876 // **********************************************************************************
877 /* We get called here when power has successfully changed state. */
878 IOReturn
879 IOSCSIHDDrive::powerStateDidChangeTo(unsigned long,unsigned long stateOrdinal,IOService*)
880 {
881 IOReturn result;
882
883 /* If we must restore the electronics state, do it now. */
884
885 if (_restoreState) {
886 result = restoreElectronicsState();
887 _restoreState = false;
888 }
889
890 /* If we have powered up into a state that can execute commands, release any queued
891 * requests that were awaiting the power change.
892 */
893
894 if (stateOrdinal > kAllOff) {
895 dequeueCommands();
896 }
897
898 return IOPMAckImplied;
899 }