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