2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 /* Sym8xxClient.m created by russb2 on Sat 30-May-1998 */
25 #include "Sym8xxController.h"
27 extern pmap_t kernel_pmap
;
30 /*-----------------------------------------------------------------------------*
32 *-----------------------------------------------------------------------------*/
33 void Sym8xxSCSIController::executeCommand( IOSCSIParallelCommand
*scsiCommand
)
37 SCSITargetLun targetLun
;
43 srb
= (SRB
*) scsiCommand
->getCommandData();
44 bzero( srb
, sizeof(SRB
) );
46 srb
->srbPhys
= (SRB
*) pmap_extract( kernel_pmap
, (vm_offset_t
) srb
);
47 srb
->scsiCommand
= scsiCommand
;
49 scsiCommand
->getCDB( &scsiCDB
);
50 scsiCommand
->getTargetLun( &targetLun
);
53 nexusPhys
= &srb
->srbPhys
->nexus
;
55 srb
->target
= targetLun
.target
;
56 srb
->lun
= targetLun
.lun
;
57 srb
->srbCDBFlags
= scsiCDB
.cdbFlags
;
60 * Setup the Nexus struct. This part of the SRB is read/written both by the
61 * script and the driver.
63 nexus
->targetParms
.target
= srb
->target
;
65 // printf( "SCSI(Symbios8xx): executeCommand: T/L = %d:%d Cmd = %08x CmdType = %d\n\r",
66 // targetLun.target, targetLun.lun, (int)scsiCommand, scsiCommand->getCmdType() );
68 switch ( scsiCommand
->getCmdType() )
70 case kSCSICommandAbort
:
71 case kSCSICommandAbortAll
:
72 case kSCSICommandDeviceReset
:
73 Sym8xxAbortCommand( scsiCommand
);
81 * Set client data buffer pointers in the SRB
83 scsiCommand
->getPointers( &srb
->xferDesc
, &srb
->xferCount
, &isWrite
);
85 srb
->directionMask
= (isWrite
) ? 0x00000000 :0x01000000;
87 nexus
->cdb
.ppData
= OSSwapHostToLittleInt32((UInt32
)&nexusPhys
->cdbData
);
89 len
= scsiCDB
.cdbLength
;
91 nexus
->cdb
.length
= OSSwapHostToLittleInt32( len
);
92 nexus
->cdbData
= scsiCDB
.cdb
;
94 Sym8xxCalcMsgs( scsiCommand
);
97 * Setup initial data transfer list (SGList)
99 nexus
->ppSGList
= (SGEntry
*)OSSwapHostToLittleInt32((UInt32
)&nexusPhys
->sgListData
[2]);
100 Sym8xxUpdateSGList( srb
);
102 Sym8xxStartSRB( srb
);
106 /*-----------------------------------------------------------------------------*
107 * This routine queues an SRB to reset the SCSI Bus
109 *-----------------------------------------------------------------------------*/
110 void Sym8xxSCSIController::resetCommand( IOSCSIParallelCommand
*scsiCommand
)
114 // printf( "SCSI(Symbios8xx): resetCommand\n\r" );
116 srb
= (SRB
*) scsiCommand
->getCommandData();
117 bzero( srb
, sizeof(SRB
) );
119 srb
->srbPhys
= (SRB
*) pmap_extract( kernel_pmap
, (vm_offset_t
) srb
);
120 srb
->scsiCommand
= scsiCommand
;
122 Sym8xxSCSIBusReset( srb
);
125 /*-----------------------------------------------------------------------------*
127 *-----------------------------------------------------------------------------*/
128 void Sym8xxSCSIController::cancelCommand( IOSCSIParallelCommand
*scsiCommand
)
130 IOSCSIParallelCommand
*origCommand
;
132 SCSITargetLun targetLun
;
133 SCSIResults scsiResults
;
135 origCommand
= scsiCommand
->getOriginalCmd();
136 srb
= (SRB
*)origCommand
->getCommandData();
138 switch ( origCommand
->getCmdType() )
140 case kSCSICommandAbort
:
141 case kSCSICommandAbortAll
:
142 case kSCSICommandDeviceReset
:
143 if ( abortSRB
== srb
)
145 SCRIPT_VAR(R_ld_AbortBdr_mailbox
) = 0;
148 origCommand
->complete();
154 if ( adapter
->nexusPtrsVirt
[srb
->nexus
.tag
] == &srb
->nexus
)
156 adapter
->nexusPtrsVirt
[srb
->nexus
.tag
] = (Nexus
*) -1;
157 adapter
->nexusPtrsPhys
[srb
->nexus
.tag
] = (Nexus
*) -1;
159 origCommand
->complete();
163 origCommand
->getTargetLun( &targetLun
);
164 origCommand
->complete();
166 IOLog( "SCSI(Symbios8xx): Aborted SRB not found - T/L = %d:%d\n\r", targetLun
.target
, targetLun
.lun
);
170 bzero( &scsiResults
, sizeof(scsiResults
) );
171 scsiCommand
->setResults( &scsiResults
);
172 scsiCommand
->complete();
175 /*-----------------------------------------------------------------------------*
180 *-----------------------------------------------------------------------------*/
181 void Sym8xxSCSIController::Sym8xxAbortCommand( IOSCSIParallelCommand
*scsiCommand
)
185 SCSITargetLun targetLun
;
188 scsiCommand
->getTargetLun( &targetLun
);
190 switch ( scsiCommand
->getCmdType() )
192 case kSCSICommandAbort
:
193 srb
= (SRB
*)scsiCommand
->getOriginalCmd()->getCommandData();
194 Sym8xxCancelMailBox( &srb
->srbPhys
->nexus
);
197 case kSCSICommandAbortAll
:
198 Sym8xxCancelMailBox( targetLun
.target
, targetLun
.lun
, false );
201 case kSCSICommandDeviceReset
:
202 Sym8xxCancelMailBox( targetLun
.target
, (UInt32
) -1, false );
211 abortReqPending
= true;
213 rescheduleCommand( scsiCommand
);
218 scsiCommand
->getCDB( &scsiCDB
);
220 srb
= (SRB
*) scsiCommand
->getCommandData();
222 srb
->nexus
.msgData
[0] = srb
->lun
| ((srb
->srbCDBFlags
& kCDBFlagsNoDisconnect
) ? 0x80 : 0xC0);
224 if ( scsiCDB
.cdbTagMsg
!= 0 )
226 srb
->nexus
.tag
= scsiCDB
.cdbTag
+ 128;
227 srb
->nexus
.msgData
[1] = srb
->nexus
.tag
;
231 srb
->nexus
.tag
= ((UInt32
)srb
->target
<< 3) | srb
->lun
;
232 srb
->nexus
.msgData
[1] = 0;
234 srb
->tag
= srb
->nexus
.tag
;
236 srb
->nexus
.msgData
[2] = scsiCDB
.cdbAbortMsg
;
238 Sym8xxAbortBdr( srb
);
242 /*-----------------------------------------------------------------------------*
243 * This routine creates SCSI messages to send during the initial connection
244 * to the target. It is called during client request processing and also by
245 * the I/O thread when a request sense operation is required.
247 * Outbound messages are setup in the MsgOut buffer in the Nexus structure of
250 *-----------------------------------------------------------------------------*/
251 void Sym8xxSCSIController::Sym8xxCalcMsgs( IOSCSIParallelCommand
*scsiCommand
)
258 SCSITargetParms targetParms
;
263 srb
= (SRB
*)scsiCommand
->getCommandData();
265 nexusPhys
= &srb
->srbPhys
->nexus
;
267 scsiCommand
->getCDB( &scsiCDB
);
270 * Setup Identify message
273 nexus
->msg
.ppData
= OSSwapHostToLittleInt32((UInt32
)&nexusPhys
->msgData
);
274 nexus
->msgData
[msgIndex
++] = srb
->lun
| (( scsiCDB
.cdbFlags
& kCDBFlagsNoDisconnect
) ? 0x80 : 0xC0);
277 * Allocate tag for request.
279 * For non-tagged requests a pseudo-tag is created consisting of target*16+lun. For tagged
280 * requests a tag in the range 128-255 is allocated.
282 * If a pseudo-tag is inuse for a non-tagged command or there are no tags available for
283 * a tagged request, then the command is blocked until a tag becomes available.
285 * Note: If we are being called during request sense processing (srbState != ksrbStateCDBDone)
286 * then a tag has already been allocated to the request.
288 if ( scsiCDB
.cdbTagMsg
!= 0 )
290 nexus
->msgData
[msgIndex
++] = scsiCDB
.cdbTagMsg
;
291 nexus
->msgData
[msgIndex
++] = srb
->tag
= srb
->nexus
.tag
= scsiCDB
.cdbTag
+ 128;
295 srb
->tag
= srb
->nexus
.tag
= ((UInt32
)srb
->target
<< 3) | srb
->lun
;
298 * Setup to negotiate for Wide (16-bit) data transfers
300 * Note: There is no provision to negotiate back to narrow transfers although
301 * SCSI does support this.
304 scsiCommand
->getDevice(kIOSCSIParallelDevice
)->getTargetParms( &targetParms
);
306 if ( scsiCDB
.cdbFlags
& (kCDBFlagsNegotiateWDTR
| kCDBFlagsNegotiateSDTR
) )
308 negotiateWDTRComplete
= negotiateSDTRComplete
= false;
311 if ( scsiCDB
.cdbFlags
& kCDBFlagsNegotiateWDTR
)
313 nexus
->msgData
[msgIndex
++] = kSCSIMsgExtended
;
314 nexus
->msgData
[msgIndex
++] = 2;
315 nexus
->msgData
[msgIndex
++] = kSCSIMsgWideDataXferReq
;
317 for ( tw
= targetParms
.transferWidth
, i
= (UInt32
)-1;
322 nexus
->msgData
[msgIndex
++] = i
;
326 * Setup to negotiate for Synchronous data transfers.
328 * Note: We can negotiate back to async based on the flags in the command.
331 if ( scsiCDB
.cdbFlags
& kCDBFlagsNegotiateSDTR
)
333 nexus
->msgData
[msgIndex
++] = kSCSIMsgExtended
;
334 nexus
->msgData
[msgIndex
++] = 3;
335 nexus
->msgData
[msgIndex
++] = kSCSIMsgSyncXferReq
;
336 if ( targetParms
.transferOffset
!= 0 )
338 nexus
->msgData
[msgIndex
++] = targetParms
.transferPeriodpS
/ 4000;
339 nexus
->msgData
[msgIndex
++] = targetParms
.transferOffset
;
343 nexus
->msgData
[msgIndex
++] = 0;
344 nexus
->msgData
[msgIndex
++] = 0;
350 * If we are negotiating for both Sync and Wide data transfers, we setup both messages
351 * in the Nexus msgOut buffer. However, after each message the script needs to wait for
352 * a reply message from the target. In this case, we set the msgOut length to include
353 * bytes upto the end of the Wide message. When we get the reply from the target, the
354 * routine handling the WDTR will setup the Nexus pointers/counts to send the remaining
355 * message bytes. See Sym8xxExecute.m(Sym8xxNegotiateWDTR).
357 srb
->srbMsgLength
= msgIndex
;
359 if ((scsiCDB
.cdbFlags
& (kCDBFlagsNegotiateWDTR
| kCDBFlagsNegotiateSDTR
))
360 == (kCDBFlagsNegotiateWDTR
| kCDBFlagsNegotiateSDTR
))
365 nexus
->msg
.length
= OSSwapHostToLittleInt32( msgIndex
);
367 srb
->srbCDBFlags
= scsiCDB
.cdbFlags
;
370 /*-----------------------------------------------------------------------------*
371 * This routine sets up the data transfer SG list for the client's buffer in the
374 * The SGList actually consists of script instructions. The script will branch
375 * to the SGList when the target enters data transfer phase. When the SGList completes
376 * it will either execute a script INT instruction if there are more segments of the
377 * user buffer that need to be transferred or will execute a script RETURN instruction
378 * to return to the script.
380 * The first two slots in the SGList are reserved for partial data transfers. See
381 * Sym8xxExecute.m(Sym8xxAdjustDataPtrs).
383 *-----------------------------------------------------------------------------*/
386 /*-----------------------------------------------------------------------------*
387 * Build SG list based on an IOMemoryDescriptor object.
389 *-----------------------------------------------------------------------------*/
390 bool Sym8xxSCSIController::Sym8xxUpdateSGList( SRB
*srb
)
392 IOPhysicalSegment range
;
399 offset
= srb
->xferOffset
;
400 bytesLeft
= srb
->xferCount
- srb
->xferOffset
;
402 if ( bytesLeft
== 0 ) return rc
;
406 while ( (bytesLeft
> 0) && (i
< MAX_SGLIST_ENTRIES
-1))
408 actRanges
= memoryCursor
->getPhysicalSegments( srb
->xferDesc
,
413 if ( actRanges
!= 1 )
420 * Note: The script instruction(s) to transfer data to/from the scsi bus
421 * have the same format as a typical SGList with the transfer length
422 * as the first word and the physical transfer address as the second.
423 * The data transfer direction is specified by a bit or'd into the
424 * high byte of the SG entry's length field.
426 srb
->nexus
.sgListData
[i
].physAddr
= OSSwapHostToLittleInt32( (UInt32
)range
.location
);
427 srb
->nexus
.sgListData
[i
].length
= OSSwapHostToLittleInt32( range
.length
| srb
->directionMask
);
429 bytesLeft
-= range
.length
;
430 offset
+= range
.length
;
436 srb
->nexus
.sgListData
[i
].length
= OSSwapHostToLittleInt32( 0x90080000 );
437 srb
->nexus
.sgListData
[i
].physAddr
= OSSwapHostToLittleInt32( 0x00000000 );
441 srb
->nexus
.sgListData
[i
].length
= OSSwapHostToLittleInt32( 0x98080000 );
442 srb
->nexus
.sgListData
[i
].physAddr
= OSSwapHostToLittleInt32( A_sglist_complete
);
445 srb
->xferOffsetPrev
= srb
->xferOffset
;
446 srb
->xferOffset
= offset
;