]>
Commit | Line | Data |
---|---|---|
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 | |
30 | OSDefineMetaClass(IOBasicSCSI,IOService) | |
31 | OSDefineAbstractStructors(IOBasicSCSI,IOService) | |
32 | ||
33 | void 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 | */ | |
38 | struct IOBasicSCSI::context * | |
39 | IOBasicSCSI::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 | ||
100 | IOReturn | |
101 | IOBasicSCSI::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 | ||
113 | IOReturn | |
114 | IOBasicSCSI::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 | ||
126 | IOReturn | |
127 | IOBasicSCSI::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 | ||
139 | UInt32 | |
140 | IOBasicSCSI::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 | ||
170 | UInt32 | |
171 | IOBasicSCSI::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 | ||
201 | void | |
202 | IOBasicSCSI::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 | ||
235 | void | |
236 | IOBasicSCSI::deleteInquiryBuffer(UInt8 *buf,UInt32 size) | |
237 | { | |
238 | IOFree((void *)buf,size); | |
239 | } | |
240 | ||
241 | void | |
242 | IOBasicSCSI::deleteTempBuffer(UInt8 *buf,UInt32 len) | |
243 | { | |
244 | IOFree((void *)buf,len); | |
245 | } | |
246 | ||
247 | void | |
248 | IOBasicSCSI::deleteReadCapacityBuffer(UInt8 *buf,UInt32 len) | |
249 | { | |
250 | IOFree((void *)buf,len); | |
251 | } | |
252 | ||
253 | IOReturn | |
254 | IOBasicSCSI::doInquiry(UInt8 *inqBuf,UInt32 maxLen,UInt32 *actualLen) | |
255 | { | |
256 | _provider->getInquiryData( inqBuf, maxLen, actualLen ); | |
257 | return kIOReturnSuccess; | |
258 | } | |
259 | ||
260 | IOReturn | |
261 | IOBasicSCSI::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 | ||
340 | void | |
341 | IOBasicSCSI::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 | ||
368 | void | |
369 | IOBasicSCSI_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 | ||
379 | void | |
380 | IOBasicSCSI::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 | ||
390 | void | |
391 | IOBasicSCSI::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 | ||
406 | void | |
407 | IOBasicSCSI::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 | ||
421 | void | |
422 | IOBasicSCSI::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 | ||
431 | bool | |
432 | IOBasicSCSI::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 | ||
468 | void | |
469 | IOBasicSCSI::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 | ||
485 | void | |
486 | IOBasicSCSI::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 | ||
500 | void | |
501 | IOBasicSCSI::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 | ||
515 | void | |
516 | IOBasicSCSI::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 | ||
524 | bool | |
525 | IOBasicSCSI::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 | ||
565 | bool | |
566 | IOBasicSCSI::customAutomaticRetry(struct IOBasicSCSI::context *cx) | |
567 | { | |
568 | return(false); /* the default does nothing special */ | |
569 | } | |
570 | ||
571 | void | |
572 | IOBasicSCSI::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 | ||
614 | char * | |
615 | IOBasicSCSI::getAdditionalDeviceInfoString(void) | |
616 | { | |
617 | return("[SCSI]"); | |
618 | } | |
619 | ||
620 | UInt64 | |
621 | IOBasicSCSI::getBlockSize(void) | |
622 | { | |
623 | return(_blockSize); | |
624 | } | |
625 | ||
626 | char * | |
627 | IOBasicSCSI::getProductString(void) | |
628 | { | |
629 | return(_product); | |
630 | } | |
631 | ||
632 | char * | |
633 | IOBasicSCSI::getRevisionString(void) | |
634 | { | |
635 | return(_rev); | |
636 | } | |
637 | ||
638 | char * | |
639 | IOBasicSCSI::getVendorString(void) | |
640 | { | |
641 | return(_vendor); | |
642 | } | |
643 | ||
644 | bool | |
645 | IOBasicSCSI::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 | ||
672 | IOReturn | |
673 | IOBasicSCSI::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 | ||
694 | IOService * | |
695 | IOBasicSCSI::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 | ||
770 | void | |
771 | IOBasicSCSI::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 | ||
812 | void | |
813 | IOBasicSCSI::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 | ||
870 | IOReturn | |
871 | IOBasicSCSI::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 | ||
890 | IOReturn | |
891 | IOBasicSCSI::reportEjectability(bool *isEjectable) | |
892 | { | |
893 | *isEjectable = true; /* default: if it's removable, it's ejectable */ | |
894 | return(kIOReturnSuccess); | |
895 | } | |
896 | ||
897 | IOReturn | |
898 | IOBasicSCSI::reportLockability(bool *isLockable) | |
899 | { | |
900 | *isLockable = true; /* default: if it's removable, it's lockable */ | |
901 | return(kIOReturnSuccess); | |
902 | } | |
903 | ||
904 | IOReturn | |
905 | IOBasicSCSI::reportMaxReadTransfer (UInt64 blocksize,UInt64 *max) | |
906 | { | |
907 | *max = blocksize * 65536; /* max blocks in a SCSI transfer */ | |
908 | return(kIOReturnSuccess); | |
909 | } | |
910 | ||
911 | IOReturn | |
912 | IOBasicSCSI::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 | ||
930 | IOReturn | |
931 | IOBasicSCSI::reportMaxWriteTransfer(UInt64 blocksize,UInt64 *max) | |
932 | { | |
933 | *max = blocksize * 65536; /* max blocks in a SCSI transfer */ | |
934 | return(kIOReturnSuccess); | |
935 | } | |
936 | ||
937 | IOReturn | |
938 | IOBasicSCSI::reportPollRequirements(bool *pollRequired,bool *pollIsExpensive) | |
939 | { | |
940 | *pollIsExpensive = false; | |
941 | *pollRequired = _removable; /* for now, all removables need polling */ | |
942 | return(kIOReturnSuccess); | |
943 | } | |
944 | ||
945 | IOReturn | |
946 | IOBasicSCSI::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 | */ | |
967 | IOReturn | |
968 | IOBasicSCSI::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 | ||
1043 | IOReturn | |
1044 | IOBasicSCSI::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 | ||
1076 | IOReturn | |
1077 | IOBasicSCSI::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 | ||
1135 | IOReturn | |
1136 | IOBasicSCSI::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 | ||
1212 | IOReturn | |
1213 | IOBasicSCSI::standardAsyncReadWriteExecute(struct context *cx) | |
1214 | { | |
1215 | return(cx->scsireq->execute()); | |
1216 | } | |
1217 | ||
1218 | IOReturn | |
1219 | IOBasicSCSI::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 | ||
1288 | bool | |
1289 | IOBasicSCSI::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 | ||
1315 | char * | |
1316 | IOBasicSCSI::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 | } |