]> git.saurik.com Git - apple/xnu.git/blame - iokit/Families/IOSCSIHDDrive/IOBasicSCSI.cpp
xnu-124.13.tar.gz
[apple/xnu.git] / iokit / Families / IOSCSIHDDrive / IOBasicSCSI.cpp
CommitLineData
1c79356b
A
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/scsi/scsi-device/SCSIDevice.h>
27#include <IOKit/storage/scsi/IOBasicSCSI.h>
28
29#define super IOService
30OSDefineMetaClass(IOBasicSCSI,IOService)
31OSDefineAbstractStructors(IOBasicSCSI,IOService)
32
33void IOBasicSCSI_gc_glue(void *object,void *param);
34
35/* Allocate a new context struct. A return of NULL means we couldn't
36 * allocate either the context itself or one of its members.
37 */
38struct IOBasicSCSI::context *
39IOBasicSCSI::allocateContext(void)
40{
41 struct context *cx;
42
43 //xxx IOLog("allocateContext entered\n");
44
45 /* First, the context structure itself. */
46
47 cx = IONew(struct context,1);
48 if (cx == NULL) {
49 return(NULL);
50 }
51
52 bzero(cx,sizeof(struct context));
53
54 /* Allocate all the structs and objects we need. If any allocation
55 * fails, we can simply call deleteContext() to free anything
56 * allocated so far.
57 */
58
59 cx->scsireq = _provider->allocCommand(kIOSCSIDevice, 0);
60 if (cx->scsireq == NULL) {
61 deleteContext(cx);
62 return(NULL);
63 }
64
65
66 /* Preset the completion parameters, which are the same for
67 * all SCSI requests we issue. Only the target function changes.
68 */
69
70 cx->senseData = (SCSISenseData *)IOMalloc(256);
71 if (cx-> senseData == NULL) {
72 deleteContext(cx);
73 return(NULL);
74 }
75
76 bzero(cx->senseData, 256 );
77
78 cx->senseDataDesc = IOMemoryDescriptor::withAddress(cx->senseData,
79 256,
80 kIODirectionIn);
81
82
83 cx->sync = IOSyncer::create(false);
84 if (cx->sync == NULL) {
85 deleteContext(cx);
86 return(NULL);
87 }
88
89 cx->retryInProgress = false;
90
91 /* We defer allocation of the Memory Descriptor till later;
92 * it will be allocated where it's needed.
93 */
94
95 // IOLog("allocateContext returning cx = %08x\n",(unsigned int)cx);
96
97 return(cx);
98}
99
100IOReturn
101IOBasicSCSI::allocateInquiryBuffer(UInt8 **buf,UInt32 size)
102{
103 *buf = (UInt8 *)IOMalloc(size);
104 if (*buf == NULL) {
105 return(kIOReturnNoMemory);
106 }
107
108 bzero(*buf,size);
109
110 return(kIOReturnSuccess);
111}
112
113IOReturn
114IOBasicSCSI::allocateTempBuffer(UInt8 **buf,UInt32 size)
115{
116 *buf = (UInt8 *)IOMalloc(size);
117 if (*buf == NULL) {
118 return(kIOReturnNoMemory);
119 }
120
121 bzero(*buf,size);
122
123 return(kIOReturnSuccess);
124}
125
126IOReturn
127IOBasicSCSI::allocateReadCapacityBuffer(UInt8 **buf,UInt8 size)
128{
129 *buf = (UInt8 *)IOMalloc(size);
130 if (*buf == NULL) {
131 return(kIOReturnNoMemory);
132 }
133
134 bzero(*buf,size);
135
136 return(kIOReturnSuccess);
137}
138
139UInt32
140IOBasicSCSI::createReadCdb(UInt8 *cdb,UInt32 *cdbLength,
141 UInt32 block,UInt32 nblks,
142 UInt32 *maxAutoSenseLength,
143 UInt32 *timeoutSeconds)
144{
145 struct IORWcdb *c;
146
147 c = (struct IORWcdb *)cdb;
148
149 c->opcode = SOP_READ10;
150 c->lunbits = 0;
151
152 c->lba_3 = block >> 24;
153 c->lba_2 = block >> 16;
154 c->lba_1 = block >> 8;
155 c->lba_0 = block & 0xff;
156
157 c->reserved = 0;
158
159 c->count_msb = nblks >> 8;
160 c->count_lsb = nblks & 0xff;
161
162 c->ctlbyte = 0;
163
164 *cdbLength = 10;
165 *maxAutoSenseLength = 8; /* do the sense */
166 *timeoutSeconds = 60;
167 return(0);
168}
169
170UInt32
171IOBasicSCSI::createWriteCdb(UInt8 *cdb,UInt32 *cdbLength,
172 UInt32 block,UInt32 nblks,
173 UInt32 *maxAutoSenseLength,
174 UInt32 *timeoutSeconds)
175{
176 struct IORWcdb *c;
177
178 c = (struct IORWcdb *)cdb;
179
180 c->opcode = SOP_WRITE10;
181 c->lunbits = 0;
182
183 c->lba_3 = block >> 24;
184 c->lba_2 = block >> 16;
185 c->lba_1 = block >> 8;
186 c->lba_0 = block & 0xff;
187
188 c->reserved = 0;
189
190 c->count_msb = nblks >> 8;
191 c->count_lsb = nblks & 0xff;
192
193 c->ctlbyte = 0;
194
195 *cdbLength = 10;
196 *maxAutoSenseLength = sizeof( SCSISenseData ); /* do the sense */
197 *timeoutSeconds = 60;
198 return(0);
199}
200
201void
202IOBasicSCSI::deleteContext(struct context *cx)
203{
204 // IOLog("deleteContext %08x\n",(unsigned int)cx);
205
206 if (cx->scsireq) {
207 cx->scsireq->release();
208 }
209
210// if (cx->scsiresult) {
211// IODelete(cx->scsiresult,struct IOSCSIResult,1);
212// }
213
214 if (cx->senseData)
215 {
216 IOFree( cx->senseData, 256 );
217 }
218
219 if ( cx->senseDataDesc )
220 {
221 cx->senseDataDesc->release();
222 }
223
224 if (cx->memory) {
225 cx->memory->release();
226 }
227
228 if (cx->sync) {
229 cx->sync->release();
230 }
231
232 IODelete(cx,struct context,1);
233}
234
235void
236IOBasicSCSI::deleteInquiryBuffer(UInt8 *buf,UInt32 size)
237{
238 IOFree((void *)buf,size);
239}
240
241void
242IOBasicSCSI::deleteTempBuffer(UInt8 *buf,UInt32 len)
243{
244 IOFree((void *)buf,len);
245}
246
247void
248IOBasicSCSI::deleteReadCapacityBuffer(UInt8 *buf,UInt32 len)
249{
250 IOFree((void *)buf,len);
251}
252
253IOReturn
254IOBasicSCSI::doInquiry(UInt8 *inqBuf,UInt32 maxLen,UInt32 *actualLen)
255{
256 _provider->getInquiryData( inqBuf, maxLen, actualLen );
257 return kIOReturnSuccess;
258}
259
260IOReturn
261IOBasicSCSI::doReadCapacity(UInt64 *blockSize,UInt64 *maxBlock)
262{
263 struct context *cx;
264 struct IOReadCapcdb *c;
265 IOSCSICommand *req;
266 SCSICDBInfo scsiCDB;
267 UInt8 *buf;
268 IOReturn result;
269
270 cx = allocateContext();
271 if (cx == NULL) {
272 return(kIOReturnNoMemory);
273 }
274
275 req = cx->scsireq;
276
277 bzero( &scsiCDB, sizeof(SCSICDBInfo) );
278
279 c = (struct IOReadCapcdb *)&scsiCDB.cdb;
280 c->opcode = SOP_READCAP;
281 c->lunbits = 0;
282 c->lba_3 = 0;
283 c->lba_2 = 0;
284 c->lba_1 = 0;
285 c->lba_0 = 0;
286 c->reserved1 = 0;
287 c->reserved2 = 0;
288 c->reserved3 = 0;
289 c->ctlbyte = 0;
290
291 scsiCDB.cdbLength = 10;
292
293 req->setCDB( &scsiCDB );
294 req->setPointers( cx->senseDataDesc, sizeof(SCSISenseData), false, true );
295
296 req->setTimeout( 30000 );
297
298 *blockSize = 0;
299 *maxBlock = 0;
300
301 result = allocateReadCapacityBuffer(&buf,kReadCapSize);
302
303 if (result == kIOReturnSuccess) {
304
305 cx->memory = IOMemoryDescriptor::withAddress((void *)buf,
306 kReadCapSize,
307 kIODirectionIn);
308
309 req->setPointers( cx->memory, kReadCapSize, false );
310
311 /* We force the drive to be completely powered-up, including the mechanical
312 * components, because some drives (e.g. CDs) access the media.
313 */
314
315 queueCommand(cx,kSync,getReadCapacityPowerState()); /* queue the operation, sleep awaiting power */
316
317 result = simpleSynchIO(cx);
318
319 if (result == kIOReturnSuccess) {
320
321 *blockSize = (buf[4] << 24) | /* endian-neutral */
322 (buf[5] << 16) |
323 (buf[6] << 8) |
324 (buf[7] );
325
326 *maxBlock = (buf[0] << 24) | /* endian-neutral */
327 (buf[1] << 16) |
328 (buf[2] << 8) |
329 (buf[3] );
330 }
331
332 deleteReadCapacityBuffer(buf,kReadCapSize);
333 }
334
335 deleteContext(cx);
336
337 return(result);
338}
339
340void
341IOBasicSCSI::free(void)
342{
343 if (_inqBuf) {
344 deleteInquiryBuffer(_inqBuf,_inqBufSize);
345 _inqBuf = NULL;
346 }
347
348#ifdef DISKPM
349 if (_powerQueue.lock) {
350 IOLockFree(_powerQueue.lock);
351 }
352#endif
353
354 if (_busResetContext) {
355 deleteContext(_busResetContext);
356 }
357 if (_unitAttentionContext) {
358 deleteContext(_unitAttentionContext);
359 }
360
361 super::free();
362}
363
364/* The Callback (C) entry from the SCSI provider. We just glue
365 * right into C++.
366 */
367
368void
369IOBasicSCSI_gc_glue(void *object,void *param)
370{
371 IOBasicSCSI *self;
372 struct IOBasicSCSI::context *cx;
373
374 self = (IOBasicSCSI *)object;
375 cx = (struct IOBasicSCSI::context *)param;
376 self->genericCompletion(cx); /* do it in C++ */
377}
378
379void
380IOBasicSCSI::setupBusResetRecovery(void)
381{
382 IOLog("%s[IOBasicSCSI]: SCSI bus reset occurred; begin recovery.\n",getName());
383
384 _busResetContext->step = 1;
385 _busResetRecoveryInProgress = true;
386 _provider->holdQueue(kQTypeNormalQ);
387 // _provider->flushQueue(kQTypeNormalQ,kIOReturnAborted);
388}
389
390void
391IOBasicSCSI::beginBusResetRecovery(void)
392{
393 /* In this method, we issue the first command necessary to recover
394 * from the Bus Reset condition. Its completion will call
395 * busResetRecoveryCommandComplete, which is respnsible for starting
396 * the next command, until all have been executed.
397 *
398 * The default implementation of this method does nothing, except
399 * to call finishBusResetRecovery immediately.
400 */
401
402 // IOLog("%s[IOBasicSCSI]: beginBusReset\n",getName());
403 finishBusResetRecovery();
404}
405
406void
407IOBasicSCSI::busResetRecoveryCommandComplete(struct IOBasicSCSI::context *cx)
408{
409 /* We are entered for each command completion during bus reset recovery.
410 *
411 * Do whatever we have to upon completion of one of our commands.
412 *
413 * Typically we would increment "step" then start another asynchronous
414 * command. When we have finished running off the whole set of required
415 * operations then we call finishBusResetRecovery.
416 *
417 * The default implementation does nothing.
418 */
419}
420
421void
422IOBasicSCSI::finishBusResetRecovery(void)
423{
424 /* Release the IO queue so that any pending commands can start. */
425
426 IOLog("%s[IOBasicSCSI]: SCSI bus reset recovery complete.\n",getName());
427 _provider->releaseQueue(kQTypeNormalQ);
428 _busResetRecoveryInProgress = false;
429}
430
431bool
432IOBasicSCSI::unitAttentionDetected(struct IOBasicSCSI::context *cx)
433{
434 SCSIResults scsiResults;
435
436 /* We're not currently handling a Unit Attention: see if
437 * we just got a one to handle. Note that we do NOT have to
438 * detect Bus Reset here, because we receive notification of
439 * that event asynchronously via the message() method.
440 */
441
442 cx->scsireq->getResults(&scsiResults);
443
444 /* A special case is Unit Attention, which can happen at any time. We begin
445 * the Unit Attention recovery procedure which issues multiple asynch commands
446 * to restore the device condition. After the recovery procedure completes,
447 * it causes a retry of the original command.
448 */
449
450 if (scsiResults.requestSenseDone == true) { /* an error occurred */
451
452 // IOLog("%s[IOBasicSCSI]::unitAttentionDetected: sense code %02x\n",
453 // getName(),cx->scsiresult->scsiSense[02]);
454
455 if ((cx->senseData->senseKey & 0x0f) == kUnitAttention) { /* it's a UA */
456
457 // IOLog("%s[IOBasicSCSI]::unitAttentionDetected: detected UnitAttention\n",
458 // getName());
459
460 return(true);
461 }
462
463 } /* no sense data, therefore NOT a Unit Attention */
464
465 return(false);
466}
467
468void
469IOBasicSCSI::setupUnitAttentionRecovery(struct IOBasicSCSI::context *cx)
470{
471 if (!_unitAttentionRecoveryInProgress) {
472
473 /* Save original IO context and set step. */
474
475 _unitAttentionContext->originalIOContext = cx;
476
477 _unitAttentionContext->step = 1;
478
479 _unitAttentionRecoveryInProgress = true;
480
481 beginUnitAttentionRecovery();
482 }
483}
484
485void
486IOBasicSCSI::beginUnitAttentionRecovery(void)
487{
488 /* In this method, we issue the first command necessary to recover
489 * from the Unit Attention condition. Its completion will call
490 * unitAttentionCommandComplete, which is respnsible for starting
491 * the next command, until all have been executed.
492 *
493 * The default implementation of this method does nothing, except
494 * to call finishUnitAttentionRecovery immediately.
495 */
496
497 finishUnitAttentionRecovery();
498}
499
500void
501IOBasicSCSI::unitAttentionRecoveryCommandComplete(struct IOBasicSCSI::context *cx)
502{
503 /* We are entered for each command completion during Unit Attention recovery.
504 *
505 * Do whatever we have to upon completion of one of our commands.
506 *
507 * Typically we would increment "step" then start another asynchronous
508 * command. When we have finished running off the whole set of required
509 * operations then we call finishUnitAttentionRecovery.
510 *
511 * The default implementation does nothing.
512 */
513}
514
515void
516IOBasicSCSI::finishUnitAttentionRecovery(void)
517{
518 /* When we're done, we reissue the command that caught the Unit Attention. */
519
520 _unitAttentionRecoveryInProgress = false;
521 _unitAttentionContext->originalIOContext->scsireq->execute();
522}
523
524bool
525IOBasicSCSI::automaticRetry(struct IOBasicSCSI::context *cx)
526{
527 SCSIResults scsiResults;
528
529 if (unitAttentionDetected(cx)) { /* do an automatic retry for Unit Attention */
530 setupUnitAttentionRecovery(cx);
531 return(true);
532 }
533
534 cx->scsireq->getResults(&scsiResults);
535
536 if (scsiResults.returnCode != kIOReturnSuccess &&
537 scsiResults.returnCode != kIOReturnError) {
538 /**
539 IOLog("%s[IOBasicSCSI]: retcode = %08lx / %s\n",
540 getName(),scsiResults.returnCode,stringFromReturn(scsiResults.returnCode));
541 **/
542 }
543
544 if (scsiResults.returnCode == kIOReturnAborted ||
545 scsiResults.returnCode == kIOReturnTimeout) { /* must be a Bus Reset abort */
546 if (!cx->retryInProgress) { /* start a retry if not already doing one */
547 cx->retryInProgress = true;
548 cx->retryCount = kMaxRetries;
549 }
550 if (cx->retryCount > 0) { /* OK to continue retrying */
551 IOLog("%s[IOBasicSCSI]: AutoRetry cx @ %08lx, cmd @ %08lx; %ld retries to go.\n",
552 getName(),(unsigned long)cx,(unsigned long)cx->scsireq,cx->retryCount);
553 cx->retryCount--;
554 cx->scsireq->execute();
555 return(true);
556 } else {
557 cx->retryInProgress = false;
558 return(false);
559 }
560 }
561
562 return(customAutomaticRetry(cx));
563}
564
565bool
566IOBasicSCSI::customAutomaticRetry(struct IOBasicSCSI::context *cx)
567{
568 return(false); /* the default does nothing special */
569}
570
571void
572IOBasicSCSI::genericCompletion(struct IOBasicSCSI::context *cx)
573{
574
575 /* We dispatch the completion depending on our state. */
576
577 // IOLog("%s[IOBasicSCSI]::genericCompletion: dispatching, state = %s\n",
578 // getName(),stringFromState(cx->state));
579
580 switch (cx->state) {
581
582 case kSimpleSynchIO :
583 if (!automaticRetry(cx)) {
584 cx->sync->signal(kIOReturnSuccess,false); /* Just wake up the waiting thread: */
585 }
586 break;
587
588 case kAsyncReadWrite : /* normal r/w completion */
589 if (!automaticRetry(cx)) {
590 RWCompletion(cx);
591 deleteContext(cx);
592 }
593 break;
594
595 case kHandlingRecoveryAfterBusReset : /* still handling recovery after reset */
596 if (!automaticRetry(cx)) {
597 busResetRecoveryCommandComplete(cx);
598 }
599 break; /* just wait for next completion */
600
601 case kHandlingUnitAttention : /* still handling UA */
602 unitAttentionRecoveryCommandComplete(cx);
603 break; /* just wait for next completion */
604
605 case kNone : /* undefined */
606 case kMaxStateValue :
607 case kAwaitingPower :
608 break;
609 }
610
611 return;
612}
613
614char *
615IOBasicSCSI::getAdditionalDeviceInfoString(void)
616{
617 return("[SCSI]");
618}
619
620UInt64
621IOBasicSCSI::getBlockSize(void)
622{
623 return(_blockSize);
624}
625
626char *
627IOBasicSCSI::getProductString(void)
628{
629 return(_product);
630}
631
632char *
633IOBasicSCSI::getRevisionString(void)
634{
635 return(_rev);
636}
637
638char *
639IOBasicSCSI::getVendorString(void)
640{
641 return(_vendor);
642}
643
644bool
645IOBasicSCSI::init(OSDictionary * properties)
646{
647 _inqBuf = NULL;
648 _inqBufSize = 0;
649 _inqLen = 0;
650
651 _vendor[8] = '\0';
652 _product[16] = '\0';
653 _rev[4] = '\0';
654
655 _readCapDone = false;
656 _blockSize = 0;
657 _maxBlock = 0;
658 _removable = false;
659
660#ifdef DISKPM
661 _powerQueue.head = NULL;
662 _powerQueue.tail = NULL;
663 _powerQueue.lock = IOLockAlloc();
664 if (_powerQueue.lock == NULL) {
665 return(false);
666 }
667#endif
668
669 return(super::init(properties));
670}
671
672IOReturn
673IOBasicSCSI::message(UInt32 type,IOService * provider,void * argument)
674{
675 // IOLog("%s[IOBasicSCSI]: message: type = %lx\n",getName(),type);
676 switch (type) {
677 case kSCSIClientMsgBusReset : /* Bus Reset has begun */
678 if (!_busResetRecoveryInProgress) { /* try to avoid reset-within-reset recovery */
679 setupBusResetRecovery(); /* indicate recovery will be in progress */
680 }
681 break; /* now wait till reset is done */
682
683 case (kSCSIClientMsgBusReset | kSCSIClientMsgDone) : /* Bus Reset is finished */
684 beginBusResetRecovery(); /* now start the actual recovery process */
685 break;
686
687 default :
688 return(super::message(type,provider,argument)); /* not one of ours */
689 }
690
691 return(kIOReturnSuccess);
692}
693
694IOService *
695IOBasicSCSI::probe(IOService * provider,SInt32 * score)
696{
697 IOReturn result;
698 OSString * string;
699
700 if (!super::probe(provider,score)) {
701 return(NULL);
702 }
703
704 _provider = (IOSCSIDevice *)provider;
705
706 /* Do an inquiry to get the device type. The inquiry buffer will
707 * be deleted by free().
708 */
709
710 _inqBufSize = kMaxInqSize;
711 result = allocateInquiryBuffer(&_inqBuf,_inqBufSize);
712 if (result != kIOReturnSuccess) {
713 return(NULL);
714 }
715
716 result = doInquiry(_inqBuf,_inqBufSize,&_inqLen);
717 if (result != kIOReturnSuccess) {
718 return(NULL);
719 }
720
721#ifdef notdef
722 // xxx NEVER match for ID=0, the boot disk. This lets us
723 // test this driver on other disk drives.
724 //
725 if (_provider->getTarget() == 0) {
726 IOLog("**%s[IOBasicSCSI]:probe; ignoring SCSI ID %d\n",
727 getName(),(int)_provider->getTarget());
728 return(NULL);
729 }
730#endif
731
732 // Fetch SCSI device information from the nub.
733
734 string = OSDynamicCast(OSString,
735 _provider->getProperty(kSCSIPropertyVendorName));
736 if (string) {
737 strncpy(_vendor, string->getCStringNoCopy(), 8);
738 _vendor[8] = '\0';
739 }
740
741 string = OSDynamicCast(OSString,
742 _provider->getProperty(kSCSIPropertyProductName));
743 if (string) {
744 strncpy(_product, string->getCStringNoCopy(), 16);
745 _product[16] = '\0';
746 }
747
748 string = OSDynamicCast(OSString,
749 _provider->getProperty(kSCSIPropertyProductRevision));
750 if (string) {
751 strncpy(_rev, string->getCStringNoCopy(), 4);
752 _rev[4] = '\0';
753 }
754
755 if (deviceTypeMatches(_inqBuf,_inqLen,score)) {
756
757/***
758 IOLog("**%s[IOBasicSCSI]::probe; accepting %s, %s, %s, %s; SCSI ID %d\n",
759 getName(),getVendorString(),getProductString(),getRevisionString(),
760 getAdditionalDeviceInfoString(),
761 (int)_provider->getTarget());
762***/
763 return(this);
764
765 } else {
766 return(NULL);
767 }
768}
769
770void
771IOBasicSCSI::dequeueCommands(void)
772{
773#ifdef DISKPM
774 struct queue *q;
775 IOReturn result;
776
777 q = &_powerQueue;
778
779 IOLockLock(q->lock);
780
781 /* Dequeue and execute all requests for which we have the proper power level. */
782
783 while (q->head) {
784 cx = q->head;
785 if (pm_vars->myCurrentState != cx->desiredPower) {
786 break;
787 }
788 q->head = cx->next; /* remove command from the queue */
789 if (q->head == NULL) {
790 q->tail = NULL;
791 }
792
793 cx->state = kNone;
794
795 /* If the queued request was synchronous, all we have to do is wake it up. */
796
797 if (cx->isSync) {
798 cx->sync->signal(kIOReturnSuccess, false); /* Just wake up the waiting thread: */
799
800 } else { /* it's async; fire it off! */
801 result = standardAsyncReadWriteExecute(cx); /* execute the async IO */
802 if (result != kIOReturnSuccess) { /* provider didn't accept it! */
803 RWCompletion(cx); /* force a completion */
804 }
805 }
806 };
807
808 IOLockUnlock(q->lock);
809#endif
810}
811
812void
813IOBasicSCSI::queueCommand(struct context *cx,bool isSync,UInt32 desiredPower)
814{
815#ifndef DISKPM //for now, just return immediately without queueing
816 /* If we're ifdefed out, we have to start async requests. Sync requests
817 * will just return immediately without any delay for power.
818 */
819 if (isSync == kAsync) {
820 (void)standardAsyncReadWriteExecute(cx); /* execute the async IO */
821 }
822#else
823 struct queue *q;
824
825 /* First, we enqueue the request to ensure sequencing with respect
826 * to other commands that may already be in the queue.
827 */
828
829 q = &_powerQueue;
830
831 cx->next = NULL;
832 cx->state = kAwaitingPower;
833
834 IOLockLock(q->lock);
835
836 if (q->head == NULL) { /* empty queue */
837 q->head = cx;
838 q->tail = q->head;
839
840 } else { /* not empty; add after tail */
841 q->tail->next = cx;
842 q->tail = cx;
843 }
844
845 /* If the command is synchronous, start by assuming we'll have to sleep
846 * awaiting power (and subsequent dequeuing). If, however, power is already
847 * right, then dequeuCommands will unlock the lock and we will continue,
848 * returning inline to the call site, exactly as if we were awakened.
849 *
850 * An async request will call dequeueCommands and always return immediately.
851 */
852
853 IOLockUnlock(q->lock);
854
855 /* Now we try to dequeue pending commands if the power's right. */
856
857 dequeueCommands();
858
859 /* If we're synchronous, we'll wait here till dequeued. If we were
860 * dequeued above (and unlocked), then we'll return to allow the
861 * caller to continue with the command execution.
862 */
863
864 if (isSync) {
865 cx->sync->wait(false); /* waits here till awakened */
866 }
867#endif //DISKPM
868}
869
870IOReturn
871IOBasicSCSI::reportBlockSize(UInt64 *blockSize)
872{
873 IOReturn result;
874
875 *blockSize = 0;
876 result = kIOReturnSuccess;
877
878 if (_readCapDone == false) {
879 result = doReadCapacity(&_blockSize,&_maxBlock);
880 _readCapDone = true;
881 }
882
883 if (result == kIOReturnSuccess) {
884 *blockSize = _blockSize;
885 }
886
887 return(result);
888}
889
890IOReturn
891IOBasicSCSI::reportEjectability(bool *isEjectable)
892{
893 *isEjectable = true; /* default: if it's removable, it's ejectable */
894 return(kIOReturnSuccess);
895}
896
897IOReturn
898IOBasicSCSI::reportLockability(bool *isLockable)
899{
900 *isLockable = true; /* default: if it's removable, it's lockable */
901 return(kIOReturnSuccess);
902}
903
904IOReturn
905IOBasicSCSI::reportMaxReadTransfer (UInt64 blocksize,UInt64 *max)
906{
907 *max = blocksize * 65536; /* max blocks in a SCSI transfer */
908 return(kIOReturnSuccess);
909}
910
911IOReturn
912IOBasicSCSI::reportMaxValidBlock(UInt64 *maxBlock)
913{
914 IOReturn result;
915
916 *maxBlock = 0;
917 result = kIOReturnSuccess;
918
919 if (_readCapDone == false) {
920 result = doReadCapacity(&_blockSize,&_maxBlock);
921 _readCapDone = true;
922 }
923
924 if (result == kIOReturnSuccess) {
925 *maxBlock = _maxBlock;
926 }
927 return(result);
928}
929
930IOReturn
931IOBasicSCSI::reportMaxWriteTransfer(UInt64 blocksize,UInt64 *max)
932{
933 *max = blocksize * 65536; /* max blocks in a SCSI transfer */
934 return(kIOReturnSuccess);
935}
936
937IOReturn
938IOBasicSCSI::reportPollRequirements(bool *pollRequired,bool *pollIsExpensive)
939{
940 *pollIsExpensive = false;
941 *pollRequired = _removable; /* for now, all removables need polling */
942 return(kIOReturnSuccess);
943}
944
945IOReturn
946IOBasicSCSI::reportRemovability(bool *isRemovable)
947{
948 if (_inqLen > 0) { /* inquiry byte exists to check */
949 if (_inqBuf[1] & 0x80) { /* it's removable */
950 *isRemovable = true;
951 _removable = true;
952 } else { /* it's not removable */
953 *isRemovable = false;
954 _removable = false;
955 }
956 } else { /* no byte? call it nonremovable */
957 *isRemovable = false;
958 }
959
960 return(kIOReturnSuccess);
961}
962
963/* Issue a Mode Sense to get the Mode Parameter Header but no pages.
964 * Since we're only interested in the Mode Parameter Header, we just
965 * issue a standard SCSI-1 6-byte command, nothing fancy.
966 */
967IOReturn
968IOBasicSCSI::reportWriteProtection(bool *writeProtected)
969{
970 struct context *cx;
971 struct IOModeSensecdb *c;
972 IOSCSICommand *req;
973 SCSICDBInfo scsiCDB;
974 SCSIResults scsiResults;
975 UInt8 *buf;
976 IOReturn result;
977
978 cx = allocateContext();
979 if (cx == NULL) {
980 return(kIOReturnNoMemory);
981 }
982
983 req = cx->scsireq;
984
985 bzero( &scsiCDB, sizeof(SCSICDBInfo) );
986
987 c = (struct IOModeSensecdb *)&scsiCDB.cdb;
988 c->opcode = SOP_MODESENSE;
989 c->lunbits = 0;
990 c->pagecode = 0 | 0x01; /* get current settings; any page will work */
991 c->reserved = 0;
992 c->len = kModeSenseSize;
993 c->ctlbyte = 0;
994
995 scsiCDB.cdbLength = 6;
996
997 req->setCDB( &scsiCDB );
998 req->setPointers( cx->senseDataDesc, sizeof(SCSISenseData), false, true );
999
1000 req->setTimeout( 30000 );
1001
1002 result = allocateTempBuffer(&buf,kModeSenseSize);
1003
1004 if (result == kIOReturnSuccess) {
1005
1006 cx->memory = IOMemoryDescriptor::withAddress((void *)buf,
1007 kModeSenseSize,
1008 kIODirectionIn);
1009
1010 req->setPointers( cx->memory, kModeSenseSize, false );
1011
1012 queueCommand(cx,kSync,getReportWriteProtectionPowerState()); /* queue the op, sleep awaiting power */
1013
1014 result = simpleSynchIO(cx);
1015
1016 if (result == kIOReturnUnderrun) {
1017 cx->scsireq->getResults( &scsiResults );
1018 if (scsiResults.bytesTransferred >= 4)
1019 result = kIOReturnSuccess;
1020 }
1021
1022 if (result == kIOReturnSuccess) {
1023 if (buf[2] & 0x80) {
1024 *writeProtected = true;
1025 } else {
1026 *writeProtected = false;
1027 }
1028 }
1029
1030 deleteTempBuffer(buf,kModeSenseSize);
1031 }
1032
1033 deleteContext(cx);
1034
1035 return(result);
1036}
1037
1038/* Issue a simple, asynchronous SCSI operation. The caller's supplied context
1039 * contains a SCSI command and Memory Descriptor. The caller is responsible
1040 * for deleting the context.
1041 */
1042
1043IOReturn
1044IOBasicSCSI::simpleAsynchIO(struct IOBasicSCSI::context *cx)
1045{
1046 IOSCSICommand *req;
1047 IOReturn result;
1048
1049 if (cx == NULL) { /* safety check */
1050 return(kIOReturnNoMemory);
1051 }
1052
1053 /* Set completion to return to genericCompletion: */
1054
1055 req = cx->scsireq;
1056 req->setCallback( (void *)this, (CallbackFn)IOBasicSCSI_gc_glue, (void *)cx );
1057
1058 cx->state = kSimpleSynchIO;
1059
1060 /* Start the scsi request: */
1061
1062 result = req->execute();
1063
1064 if (result == true ) {
1065 result = req->getResults((SCSIResults *) 0);
1066 }
1067
1068 return(result);
1069}
1070
1071/* Issue a simple, synchronous SCSI operation. The caller's supplied context
1072 * contains a SCSI command and Memory Descriptor. The caller is responsible
1073 * for deleting the context.
1074 */
1075
1076IOReturn
1077IOBasicSCSI::simpleSynchIO(struct context *cx)
1078{
1079 IOSCSICommand *req;
1080 IOReturn result;
1081
1082 if (cx == NULL) { /* safety check */
1083 return(kIOReturnNoMemory);
1084 }
1085
1086 /* Set completion to return to genericCompletion: */
1087
1088 req = cx->scsireq;
1089 req->setCallback( (void *)this, (CallbackFn)IOBasicSCSI_gc_glue, (void *)cx );
1090
1091 cx->state = kSimpleSynchIO;
1092
1093/**
1094 IOLog("%s[IOBasicSCSI]::simpleSynchIO; issuing SCSI cmd %02x\n",
1095 getName(),req->cdb.byte[0]);
1096**/
1097 /* Start the scsi request: */
1098
1099 //IOLog("IOBasicSCSI::simpleSynchIO, lock initted, calling SCSI\n");
1100
1101 result = req->execute();
1102
1103 if (result == true ) {
1104
1105// IOLog("IOBasicSCSI::simpleSynchIO, SCSI req accepted\n");
1106
1107 /* Wait for it to complete by attempting to acquire a read-lock, which
1108 * will block until the write-lock is released by the completion routine.
1109 */
1110
1111 cx->sync->wait(false); /* waits here till unlocked at completion */
1112
1113 /* We're back: */
1114
1115 result = req->getResults((SCSIResults *) 0);
1116
1117/**
1118 if ((result != kIOReturnSuccess) ) {
1119 IOLog("%s[IOBasicSCSI]::simpleSynchIO; err '%s' from completed req\n",
1120 getName(),stringFromReturn(result));
1121 }
1122**/
1123 } else {
1124/**
1125 IOLog("%s[IOBasicSCSI]:simpleSynchIO; err '%s' queueing SCSI req\n",
1126 getName(),stringFromReturn(result));
1127**/
1128 }
1129
1130// IOLog("IOBasicSCSI: completed; result '%s'\n",stringFromReturn(result));
1131
1132 return(result);
1133}
1134
1135IOReturn
1136IOBasicSCSI::standardAsyncReadWrite(IOMemoryDescriptor *buffer,
1137 UInt32 block,UInt32 nblks,
1138 IOStorageCompletion completion)
1139{
1140 struct context *cx;
1141 IOSCSICommand *req;
1142 SCSICDBInfo scsiCDB;
1143 UInt32 reqSenseLength;
1144 UInt32 timeoutSeconds;
1145 UInt8 *cdb;
1146 bool isWrite;
1147
1148 cx = allocateContext();
1149
1150 if (cx == NULL) {
1151 return(kIOReturnNoMemory);
1152 }
1153
1154 buffer->retain(); /* bump the retain count */
1155
1156 cx->memory = buffer;
1157 if (buffer->getDirection() == kIODirectionOut) {
1158 isWrite = true;
1159 } else {
1160 isWrite = false;
1161 }
1162
1163/**
1164 IOLog("%s[IOBasicSCSI]::standardAsyncReadWrite; (%s) blk %ld nblks %ld\n",
1165 getName(),(isWrite ? "write" : "read"),block,nblks);
1166**/
1167 req = cx->scsireq;
1168
1169 /* Set completion to return to rwCompletion: */
1170 cx->completion = completion;
1171
1172 bzero( &scsiCDB, sizeof(scsiCDB) );
1173
1174 req->setPointers( buffer, nblks * getBlockSize(), isWrite );
1175
1176 req->setCallback( this, IOBasicSCSI_gc_glue, cx );
1177
1178 cx->state = kAsyncReadWrite;
1179
1180 cdb = (UInt8 *) &scsiCDB.cdb;
1181
1182 /* Allow a subclass to override the creation of the cdb and specify
1183 * other parameters for the operation.
1184 */
1185
1186 if (isWrite) {
1187 scsiCDB.cdbFlags |= createWriteCdb(cdb,&scsiCDB.cdbLength,
1188 block,nblks,
1189 &reqSenseLength,
1190 &timeoutSeconds);
1191
1192 } else {
1193
1194 scsiCDB.cdbFlags |= createReadCdb(cdb,&scsiCDB.cdbLength,
1195 block,nblks,
1196 &reqSenseLength,
1197 &timeoutSeconds);
1198 }
1199
1200 req->setCDB( &scsiCDB );
1201 req->setPointers( cx->senseDataDesc, reqSenseLength, false, true );
1202 req->setTimeout( timeoutSeconds * 1000 );
1203
1204 /* Queue the request awaiting power and return. When power comes up,
1205 * the request will be passed to standardAsyncReadWriteExecute.
1206 */
1207 queueCommand(cx,kAsync,getReadWritePowerState()); /* queue and possibly wait for power */
1208
1209 return(kIOReturnSuccess);
1210}
1211
1212IOReturn
1213IOBasicSCSI::standardAsyncReadWriteExecute(struct context *cx)
1214{
1215 return(cx->scsireq->execute());
1216}
1217
1218IOReturn
1219IOBasicSCSI::standardSyncReadWrite(IOMemoryDescriptor *buffer,UInt32 block,UInt32 nblks)
1220{
1221 struct context *cx;
1222 IOSCSICommand *req;
1223 SCSICDBInfo scsiCDB;
1224 UInt32 reqSenseLength;
1225 UInt32 reqTimeoutSeconds;
1226 UInt8 *cdb;
1227 bool isWrite;
1228 IOReturn result;
1229
1230 cx = allocateContext();
1231
1232 if (cx == NULL) {
1233 return(kIOReturnNoMemory);
1234 }
1235
1236 cx->memory = buffer;
1237 buffer->retain(); /* bump the retain count */
1238
1239 if (buffer->getDirection() == kIODirectionOut) {
1240 isWrite = true;
1241 } else {
1242 isWrite = false;
1243 }
1244
1245/**
1246 IOLog("%s[IOBasicSCSI]::standardSyncReadWrite; (%s) blk %ld nblks %ld\n",
1247 getName(),(isWrite ? "write" : "read"),block,nblks);
1248**/
1249
1250 bzero(&scsiCDB,sizeof(scsiCDB));
1251
1252 req = cx->scsireq;
1253 req->setPointers(buffer,(nblks * getBlockSize()),isWrite);
1254
1255 cdb = (UInt8 *)&scsiCDB.cdb;
1256
1257 /* Allow a subclass to override the creation of the cdb and specify
1258 * other parameters for the operation.
1259 */
1260
1261 if (isWrite) {
1262 scsiCDB.cdbFlags |= createWriteCdb(cdb,&scsiCDB.cdbLength,
1263 block,nblks,
1264 &reqSenseLength,
1265 &reqTimeoutSeconds);
1266
1267 } else {
1268
1269 scsiCDB.cdbFlags |= createReadCdb(cdb,&scsiCDB.cdbLength,
1270 block,nblks,
1271 &reqSenseLength,
1272 &reqTimeoutSeconds);
1273 }
1274
1275
1276 req->setCDB(&scsiCDB);
1277 req->setPointers(cx->senseDataDesc,reqSenseLength,false,true);
1278 req->setTimeout(reqTimeoutSeconds * 1000);
1279
1280 queueCommand(cx,kSync,getReadWritePowerState()); /* queue the operation, sleep awaiting power */
1281
1282 result = simpleSynchIO(cx); /* issue a simple command */
1283
1284 deleteContext(cx);
1285 return(result);
1286}
1287
1288bool
1289IOBasicSCSI::start(IOService *provider)
1290{
1291 bool result;
1292
1293 _busResetContext = allocateContext();
1294 if (_busResetContext == NULL) {
1295 return(false);
1296 }
1297 _busResetContext->state = kHandlingRecoveryAfterBusReset;
1298 _busResetRecoveryInProgress = false;
1299
1300 _unitAttentionContext = allocateContext();
1301 if (_unitAttentionContext == NULL) {
1302 return(false);
1303 }
1304 _unitAttentionContext->state = kHandlingUnitAttention;
1305 _unitAttentionRecoveryInProgress = false;
1306
1307 result = provider->open(this,0,0); /* set up to receive message() notifications */
1308 if (result != true) {
1309 IOLog("open result is false\n");
1310 }
1311
1312 return(true);
1313}
1314
1315char *
1316IOBasicSCSI::stringFromState(stateValue state)
1317{
1318 static char *stateNames[] = {
1319 "kNone",
1320 "kAsyncReadWrite",
1321 "kSimpleSynchIO",
1322 "kHandlingUnitAttention",
1323 "khandlingRecoveryAfterBusReset"
1324 };
1325
1326 if (state < 0 || state > kMaxValidState) {
1327 return("invalid");
1328 }
1329
1330 return(stateNames[state]);
1331}