]> git.saurik.com Git - apple/xnu.git/blame - iokit/Drivers/scsi/drvSymbios8xx/Sym8xxClient.cpp
xnu-123.5.tar.gz
[apple/xnu.git] / iokit / Drivers / scsi / drvSymbios8xx / Sym8xxClient.cpp
CommitLineData
1c79356b
A
1/*
2 * Copyright (c) 1999 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/* Sym8xxClient.m created by russb2 on Sat 30-May-1998 */
24
25#include "Sym8xxController.h"
26
27extern pmap_t kernel_pmap;
28
29
30/*-----------------------------------------------------------------------------*
31 *
32 *-----------------------------------------------------------------------------*/
33void Sym8xxSCSIController::executeCommand( IOSCSIParallelCommand *scsiCommand )
34{
35 SRB *srb = NULL;
36 SCSICDBInfo scsiCDB;
37 SCSITargetLun targetLun;
38 Nexus *nexus;
39 Nexus *nexusPhys;
40 UInt32 len;
41 bool isWrite;
42
43 srb = (SRB *) scsiCommand->getCommandData();
44 bzero( srb, sizeof(SRB) );
45
46 srb->srbPhys = (SRB *) pmap_extract( kernel_pmap, (vm_offset_t) srb );
47 srb->scsiCommand = scsiCommand;
48
49 scsiCommand->getCDB( &scsiCDB );
50 scsiCommand->getTargetLun( &targetLun );
51
52 nexus = &srb->nexus;
53 nexusPhys = &srb->srbPhys->nexus;
54
55 srb->target = targetLun.target;
56 srb->lun = targetLun.lun;
57 srb->srbCDBFlags = scsiCDB.cdbFlags;
58
59 /*
60 * Setup the Nexus struct. This part of the SRB is read/written both by the
61 * script and the driver.
62 */
63 nexus->targetParms.target = srb->target;
64
65// printf( "SCSI(Symbios8xx): executeCommand: T/L = %d:%d Cmd = %08x CmdType = %d\n\r",
66// targetLun.target, targetLun.lun, (int)scsiCommand, scsiCommand->getCmdType() );
67
68 switch ( scsiCommand->getCmdType() )
69 {
70 case kSCSICommandAbort:
71 case kSCSICommandAbortAll:
72 case kSCSICommandDeviceReset:
73 Sym8xxAbortCommand( scsiCommand );
74 return;
75
76 default:
77 ;
78 }
79
80 /*
81 * Set client data buffer pointers in the SRB
82 */
83 scsiCommand->getPointers( &srb->xferDesc, &srb->xferCount, &isWrite );
84
85 srb->directionMask = (isWrite) ? 0x00000000 :0x01000000;
86
87 nexus->cdb.ppData = OSSwapHostToLittleInt32((UInt32)&nexusPhys->cdbData);
88
89 len = scsiCDB.cdbLength;
90
91 nexus->cdb.length = OSSwapHostToLittleInt32( len );
92 nexus->cdbData = scsiCDB.cdb;
93
94 Sym8xxCalcMsgs( scsiCommand );
95
96 /*
97 * Setup initial data transfer list (SGList)
98 */
99 nexus->ppSGList = (SGEntry *)OSSwapHostToLittleInt32((UInt32)&nexusPhys->sgListData[2]);
100 Sym8xxUpdateSGList( srb );
101
102 Sym8xxStartSRB( srb );
103}
104
105
106/*-----------------------------------------------------------------------------*
107 * This routine queues an SRB to reset the SCSI Bus
108 *
109 *-----------------------------------------------------------------------------*/
110void Sym8xxSCSIController::resetCommand( IOSCSIParallelCommand *scsiCommand )
111{
112 SRB *srb;
113
114// printf( "SCSI(Symbios8xx): resetCommand\n\r" );
115
116 srb = (SRB *) scsiCommand->getCommandData();
117 bzero( srb, sizeof(SRB) );
118
119 srb->srbPhys = (SRB *) pmap_extract( kernel_pmap, (vm_offset_t) srb );
120 srb->scsiCommand = scsiCommand;
121
122 Sym8xxSCSIBusReset( srb );
123}
124
125/*-----------------------------------------------------------------------------*
126 *
127 *-----------------------------------------------------------------------------*/
128void Sym8xxSCSIController::cancelCommand( IOSCSIParallelCommand *scsiCommand )
129{
130 IOSCSIParallelCommand *origCommand;
131 SRB *srb;
132 SCSITargetLun targetLun;
133 SCSIResults scsiResults;
134
135 origCommand = scsiCommand->getOriginalCmd();
136 srb = (SRB *)origCommand->getCommandData();
137
138 switch ( origCommand->getCmdType() )
139 {
140 case kSCSICommandAbort:
141 case kSCSICommandAbortAll:
142 case kSCSICommandDeviceReset:
143 if ( abortSRB == srb )
144 {
145 SCRIPT_VAR(R_ld_AbortBdr_mailbox) = 0;
146 abortSRB = 0;
147
148 origCommand->complete();
149 }
150 break;
151
152 default:
153
154 if ( adapter->nexusPtrsVirt[srb->nexus.tag] == &srb->nexus )
155 {
156 adapter->nexusPtrsVirt[srb->nexus.tag] = (Nexus *) -1;
157 adapter->nexusPtrsPhys[srb->nexus.tag] = (Nexus *) -1;
158
159 origCommand->complete();
160 }
161 else
162 {
163 origCommand->getTargetLun( &targetLun );
164 origCommand->complete();
165
166 IOLog( "SCSI(Symbios8xx): Aborted SRB not found - T/L = %d:%d\n\r", targetLun.target, targetLun.lun );
167 }
168 }
169
170 bzero( &scsiResults, sizeof(scsiResults) );
171 scsiCommand->setResults( &scsiResults );
172 scsiCommand->complete();
173}
174
175/*-----------------------------------------------------------------------------*
176 *
177 *
178 *
179 *
180 *-----------------------------------------------------------------------------*/
181void Sym8xxSCSIController::Sym8xxAbortCommand( IOSCSIParallelCommand *scsiCommand )
182{
183 SRB *srb;
184 SCSICDBInfo scsiCDB;
185 SCSITargetLun targetLun;
186
187
188 scsiCommand->getTargetLun( &targetLun );
189
190 switch ( scsiCommand->getCmdType() )
191 {
192 case kSCSICommandAbort:
193 srb = (SRB *)scsiCommand->getOriginalCmd()->getCommandData();
194 Sym8xxCancelMailBox( &srb->srbPhys->nexus );
195 break;
196
197 case kSCSICommandAbortAll:
198 Sym8xxCancelMailBox( targetLun.target, targetLun.lun, false );
199 break;
200
201 case kSCSICommandDeviceReset:
202 Sym8xxCancelMailBox( targetLun.target, (UInt32) -1, false );
203 break;
204
205 default:
206 ;
207 }
208
209 if ( abortSRB )
210 {
211 abortReqPending = true;
212
213 rescheduleCommand( scsiCommand );
214 disableCommands();
215 return;
216 }
217
218 scsiCommand->getCDB( &scsiCDB );
219
220 srb = (SRB *) scsiCommand->getCommandData();
221
222 srb->nexus.msgData[0] = srb->lun | ((srb->srbCDBFlags & kCDBFlagsNoDisconnect ) ? 0x80 : 0xC0);
223
224 if ( scsiCDB.cdbTagMsg != 0 )
225 {
226 srb->nexus.tag = scsiCDB.cdbTag + 128;
227 srb->nexus.msgData[1] = srb->nexus.tag;
228 }
229 else
230 {
231 srb->nexus.tag = ((UInt32)srb->target << 3) | srb->lun;
232 srb->nexus.msgData[1] = 0;
233 }
234 srb->tag = srb->nexus.tag;
235
236 srb->nexus.msgData[2] = scsiCDB.cdbAbortMsg;
237
238 Sym8xxAbortBdr( srb );
239}
240
241
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.
246 *
247 * Outbound messages are setup in the MsgOut buffer in the Nexus structure of
248 * the SRB.
249 *
250 *-----------------------------------------------------------------------------*/
251void Sym8xxSCSIController::Sym8xxCalcMsgs( IOSCSIParallelCommand *scsiCommand )
252{
253 SRB *srb;
254 Nexus *nexus;
255 Nexus *nexusPhys;
256 UInt32 msgIndex;
257 SCSICDBInfo scsiCDB;
258 SCSITargetParms targetParms;
259 UInt32 i;
260 UInt32 tw;
261
262
263 srb = (SRB *)scsiCommand->getCommandData();
264 nexus = &srb->nexus;
265 nexusPhys = &srb->srbPhys->nexus;
266
267 scsiCommand->getCDB( &scsiCDB );
268
269 /*
270 * Setup Identify message
271 */
272 msgIndex = 0;
273 nexus->msg.ppData = OSSwapHostToLittleInt32((UInt32)&nexusPhys->msgData);
274 nexus->msgData[msgIndex++] = srb->lun | (( scsiCDB.cdbFlags & kCDBFlagsNoDisconnect ) ? 0x80 : 0xC0);
275
276 /*
277 * Allocate tag for request.
278 *
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.
281 *
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.
284 *
285 * Note: If we are being called during request sense processing (srbState != ksrbStateCDBDone)
286 * then a tag has already been allocated to the request.
287 */
288 if ( scsiCDB.cdbTagMsg != 0 )
289 {
290 nexus->msgData[msgIndex++] = scsiCDB.cdbTagMsg;
291 nexus->msgData[msgIndex++] = srb->tag = srb->nexus.tag = scsiCDB.cdbTag + 128;
292 }
293 else
294 {
295 srb->tag = srb->nexus.tag = ((UInt32)srb->target << 3) | srb->lun;
296 }
297 /*
298 * Setup to negotiate for Wide (16-bit) data transfers
299 *
300 * Note: There is no provision to negotiate back to narrow transfers although
301 * SCSI does support this.
302 */
303
304 scsiCommand->getDevice(kIOSCSIParallelDevice)->getTargetParms( &targetParms );
305
306 if ( scsiCDB.cdbFlags & (kCDBFlagsNegotiateWDTR | kCDBFlagsNegotiateSDTR) )
307 {
308 negotiateWDTRComplete = negotiateSDTRComplete = false;
309 }
310
311 if ( scsiCDB.cdbFlags & kCDBFlagsNegotiateWDTR )
312 {
313 nexus->msgData[msgIndex++] = kSCSIMsgExtended;
314 nexus->msgData[msgIndex++] = 2;
315 nexus->msgData[msgIndex++] = kSCSIMsgWideDataXferReq;
316
317 for ( tw = targetParms.transferWidth, i = (UInt32)-1;
318 tw;
319 tw >>= 1, i++ )
320 ;
321
322 nexus->msgData[msgIndex++] = i;
323 }
324
325 /*
326 * Setup to negotiate for Synchronous data transfers.
327 *
328 * Note: We can negotiate back to async based on the flags in the command.
329 */
330
331 if ( scsiCDB.cdbFlags & kCDBFlagsNegotiateSDTR )
332 {
333 nexus->msgData[msgIndex++] = kSCSIMsgExtended;
334 nexus->msgData[msgIndex++] = 3;
335 nexus->msgData[msgIndex++] = kSCSIMsgSyncXferReq;
336 if ( targetParms.transferOffset != 0 )
337 {
338 nexus->msgData[msgIndex++] = targetParms.transferPeriodpS / 4000;
339 nexus->msgData[msgIndex++] = targetParms.transferOffset;
340 }
341 else
342 {
343 nexus->msgData[msgIndex++] = 0;
344 nexus->msgData[msgIndex++] = 0;
345 }
346
347 }
348
349 /*
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).
356 */
357 srb->srbMsgLength = msgIndex;
358
359 if ((scsiCDB.cdbFlags & (kCDBFlagsNegotiateWDTR | kCDBFlagsNegotiateSDTR))
360 == (kCDBFlagsNegotiateWDTR | kCDBFlagsNegotiateSDTR))
361 {
362 msgIndex -= 5;
363 }
364
365 nexus->msg.length = OSSwapHostToLittleInt32( msgIndex );
366
367 srb->srbCDBFlags = scsiCDB.cdbFlags;
368}
369
370/*-----------------------------------------------------------------------------*
371 * This routine sets up the data transfer SG list for the client's buffer in the
372 * Nexus structure.
373 *
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.
379 *
380 * The first two slots in the SGList are reserved for partial data transfers. See
381 * Sym8xxExecute.m(Sym8xxAdjustDataPtrs).
382 *
383 *-----------------------------------------------------------------------------*/
384
385
386/*-----------------------------------------------------------------------------*
387 * Build SG list based on an IOMemoryDescriptor object.
388 *
389 *-----------------------------------------------------------------------------*/
390bool Sym8xxSCSIController::Sym8xxUpdateSGList( SRB *srb )
391{
392 IOPhysicalSegment range;
393 UInt32 actRanges;
394 UInt32 offset;
395 UInt32 bytesLeft;
396 UInt32 i;
397 IOReturn rc = true;
398
399 offset = srb->xferOffset;
400 bytesLeft = srb->xferCount - srb->xferOffset;
401
402 if ( bytesLeft == 0 ) return rc;
403
404 i = 2;
405
406 while ( (bytesLeft > 0) && (i < MAX_SGLIST_ENTRIES-1))
407 {
408 actRanges = memoryCursor->getPhysicalSegments( srb->xferDesc,
409 offset,
410 &range,
411 1 );
412
413 if ( actRanges != 1 )
414 {
415 rc = false;
416 break;
417 }
418
419 /*
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.
425 */
426 srb->nexus.sgListData[i].physAddr = OSSwapHostToLittleInt32( (UInt32)range.location );
427 srb->nexus.sgListData[i].length = OSSwapHostToLittleInt32( range.length | srb->directionMask );
428
429 bytesLeft -= range.length;
430 offset += range.length;
431 i++;
432 }
433
434 if ( !bytesLeft )
435 {
436 srb->nexus.sgListData[i].length = OSSwapHostToLittleInt32( 0x90080000 );
437 srb->nexus.sgListData[i].physAddr = OSSwapHostToLittleInt32( 0x00000000 );
438 }
439 else
440 {
441 srb->nexus.sgListData[i].length = OSSwapHostToLittleInt32( 0x98080000 );
442 srb->nexus.sgListData[i].physAddr = OSSwapHostToLittleInt32( A_sglist_complete );
443 }
444
445 srb->xferOffsetPrev = srb->xferOffset;
446 srb->xferOffset = offset;
447
448 return rc;
449}
450