|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24:
25: /**
26: * Copyright (c) 1994-1996 NeXT Software, Inc. All rights reserved.
27: * Copyright 1993-1995 by Apple Computer, Inc., all rights reserved.
28: * Copyright 1997-1998 Apple Computer Inc. All Rights Reserved.
29: * @author Martin Minow mailto:[email protected]
30: * @revision 1997.02.13 Initial conversion from Copland sources.
31: *
32: * Set tabs every 4 characters.
33: *
34: * Edit History
35: * 1997.02.25 MM Initial conversion from Copland sources.
36: */
37:
38: #import <sys/systm.h>
39: #import <driverkit/generalFuncs.h>
40: #import <driverkit/kernelDriver.h>
41: #import <driverkit/align.h>
42: #import <driverkit/interruptMsg.h>
43: #import <driverkit/scsiTypes.h>
44: #import <driverkit/debugging.h>
45: #import <driverkit/IODirectDevice.h>
46: #import <driverkit/IOMemoryDescriptor.h>
47: #import <driverkit/IOSimpleMemoryDescriptor.h>
48: #import <driverkit/IOSCSIController.h>
49: #import <driverkit/IOPower.h>
50: #import <driverkit/return.h>
51: #import <bsd/dev/scsireg.h>
52: #import <mach/kern_return.h>
53: #import <mach/mach_interface.h>
54: #import <mach/message.h>
55: #import <machkit/NXLock.h>
56: #import <machdep/ppc/proc_reg.h>
57: #import <machdep/ppc/powermac.h>
58: #import <machdep/ppc/interrupts.h>
59: #import <machdep/ppc/dbdma.h>
60: #import <kernserv/prototypes.h>
61: #import <objc/objc.h>
62:
63: extern void flush_cache_v( vm_offset_t pa, unsigned length ); /* Should be available from kernel headers! */
64: // extern void invalidate_cache_v( vm_offset_t pa, unsigned length );
65: extern void kprintf( const char *, ... );
66: extern kern_return_t msg_send_from_kernel( msg_header_t*, int, int );
67:
68:
69: #import "MESH_DBDMA.h"
70:
71: #undef ASSERT
72: #define ASSERT(x)
73:
74: #if CustomMiniMon
75: extern globals g; /**** Use custom MiniMon's globals ****/
76: extern UInt32 gMESH_DBDMA, gMESH_DBDMA_Phys;
77: #else
78: globals g; /**** Instantiate the globals ****/
79: #endif /* CustomMiniMon */
80:
81:
82:
83: /* Channel Program. Note that this script must match the offsets */
84: /* specified in AppleMeshDefinitions.h. This script is copied into */
85: /* the channel command area (with appropriate entries byte-swapped */
86: /* so it ends up with the correct endian-ness). */
87: /* Lines beginning with "slash, star, star, slash" are modified */
88: /* by the driver before it starts the Channel Program. */
89:
90: const DBDMADescriptor gDescriptorList[] =
91: {
92: /* 0x00 kcclProblem - Branch here for anomalies */
93:
94: { MESH_REG( kMeshInterruptMask, kMeshIntrMask ) }, // Enable MESH interrupt
95: { STOP( kcclStageCCLx ) }, // anomaly
96:
97: /* 0x20 through 0x60 - Data for information phases: */
98:
99: { RESERVE }, // kcclCMDOdata - CDB ( 6,10,12,16 bytes)
100: { RESERVE }, // kcclMSGOdata - MSGO data (last byte @3F)
101: { RESERVE }, // kcclMSGIdata - MSGI data & STATUS
102: { RESERVE }, // kcclSenseCDB - CDB for (auto) Sense
103: { RESERVE }, // kcclBatchSize, kcclStageLabel
104:
105: /* 0x70 - kcclSense - AutoSense input: */
106:
107: { MESH_REG( kMeshTransferCount1, 0x00 ) }, // set MESH xfer count to 255
108: { MESH_REG( kMeshTransferCount0, kMaxAutosenseByteCount & 0xFF )},
109: { MESH_REG( kMeshSequence, kMeshDataInCmd | kMeshSeqDMA )},// Data-In to Seq register
110: { SENSE( kMaxAutosenseByteCount ) }, // Sense INPUT
111: { BRANCH( kcclGetStatus ) }, // do finish sequence
112:
113: /* 0xC0 - kcclPrototype - Prototype MESH 4-command Transfer sequence: */
114:
115: { MOVE_4( kcclBatchSize, 0, kRelAddressCP ) }, // MESH batch size
116: { MESH_REG( kMeshTransferCount1, 0 ) }, // Set high order Transfer Count
117: { MESH_REG( kMeshTransferCount0, 0 ) }, // Set low order Transfer Count
118: { MESH_REG( kMeshSequence, kMeshDataInCmd | kMeshSeqDMA )}, // Assume Data-In
119:
120: { RESERVE }, // spare
121: { RESERVE }, // spare
122:
123: /* 0x120 kcclStart - Arbitrate (START CHANNEL PROGRAM HERE): */
124: /* 0x140 kcclBrProblem */
125:
126: { STAGE( kcclStageArb ) },
127: { MESH_REG( kMeshSequence, kMeshArbitrateCmd ) }, // issue Arbitrate
128: { BR_IF_PROBLEM }, // branch if exception or error
129:
130: /* 0x150 - Select with Attention: */
131:
132: { STAGE( kcclStageSelA ) },
133: { CLEAR_CMD_DONE },
134: { MESH_REG( kMeshSequence, kMeshSelectCmd | kMeshSeqAtn ) }, // select with attention
135: { BR_IF_PROBLEM }, // branch if failed
136:
137: /* 0x190 kcclMsgoStage- Message-Out: */
138:
139: { STAGE( kcclStageMsgO ) },
140: { CLEAR_CMD_DONE },
141:
142: /* 0x1B0 kcclMsgoBranch - modify this BRANCH to fall through for multibyte messages: */
143:
144: /**/{ BRANCH( kcclLastMsgo ) }, // kcclMsgoBranch - go do only byte of Msg
145:
146: /* 0x1C0 - do all but last byte of multibyte message: */
147:
148: { MESH_REG( kMeshTransferCount1, 0x00 ) }, // count does include last byte
149: /**/{ MESH_REG( kMeshTransferCount0, 0xFF ) }, // kcclMsgoMTC - modify MESH xfer count here
150: { MESH_REG( kMeshSequence, kMeshMessageOutCmd | kMeshSeqAtn | kMeshSeqDMA ) }, // DMA MsgO with ATN
151: /**/{ MSGO( kcclMSGOdata, 255 ) }, // kcclMsgoDTC - output all but last byte
152: { CLEAR_CMD_DONE },
153:
154: /* 0x210 kcclLastMsgo - wait for REQ signal before dropping ATN: */
155:
156: { MESH_REG( kMeshInterruptMask, 0 ) }, // inhibit MESH interrupt
157: { MESH_REG_WAIT( kMeshSequence, kMeshStatusCmd | kMeshSeqAtn ) }, // gen PhaseMM
158: { CLEAR_INT_REG }, // clear PhaseMM & CmdDone
159: { MESH_REG( kMeshInterruptMask, kMeshIntrException | kMeshIntrError ) }, // re-enable ERR/EXC Ints
160:
161: /* 0x250 - put out the last or only byte of Message-Out phase: */
162:
163: { MESH_REG( kMeshTransferCount1, 0x00 ) },
164: { MESH_REG( kMeshTransferCount0, 0x01 ) },
165: { MESH_REG( kMeshSequence, kMeshMessageOutCmd | kMeshSeqDMA ) },// no more ATN
166: { MSGO( kcclMSGOLast, 1 ) },
167:
168: /* 0x290 kcclCmdoStage - Command Out: */
169:
170: { STAGE( kcclStageCmdO ) },
171: { CLEAR_CMD_DONE },
172: { MESH_REG( kMeshTransferCount1, 0x00 ) },
173: /**/{ MESH_REG( kMeshTransferCount0, 0x06 ) }, // kcclCmdoMTC - Set MESH xfer count to 6
174: { MESH_REG( kMeshSequence, kMeshCommandCmd | kMeshSeqDMA )}, // Command phase with DMA on
175: /**/{ CMDO( 6 ) }, // kcclCmdoDTC - output the CDB
176:
177: /* 0x2F0 - DATA XFER - branch to the built CCL @ 0x05D0: */
178: /* also, kcclReselect - reselect code enters here: */
179:
180: { CLEAR_CMD_DONE },
181: { STAGE( kcclStageXfer ) },
182: { BRANCH( kcclDataXfer ) }, // go do Xfer CCL
183:
184: /* 0x320 kcclOverrun - dump excess data in the bit bucket: */
185: /* Exc and Err are still disabled. */
186:
187: { STAGE( kcclStageBucket ) },
188: { MESH_REG( kMeshTransferCount1, 0x00 ) }, // set MESH Transfer Count to max
189: { MESH_REG( kMeshTransferCount0, 0x00 ) },
190: { CLR_PHASEMM },
191: { MESH_REG( kMeshInterruptMask, kMeshIntrException | kMeshIntrError ) }, // re-enable ERR/EXC Ints
192: /**/{ MESH_REG( kMeshSequence, kMeshDataInCmd | kMeshSeqDMA ) }, // set Seq Reg
193: /**/{ BUCKET }, // OUT/INPUT_LAST the bits
194: { BR_NO_PROBLEM( kcclOverrunDBDMA ) }, // loop til PhaseMismatch
195: { BR_IF_PROBLEM }, // take the interrupt now
196:
197: /* 0x3B0 kcclSyncCleanUp - clean up after Sync xfer: */
198: { CLEAR_INT_REG }, // clear PhaseMM & CmdDone (& Err?)
199: { MESH_REG( kMeshInterruptMask, kMeshIntrException | kMeshIntrError ) }, // re-enable ERR/EXC Ints
200:
201: /* 0x3D0 kcclGetStatus - setup CCL for status, command complete and bus free: */
202:
203: { STAGE( kcclStageStat ) },
204: { MESH_REG( kMeshTransferCount1, 0x00 ) },
205: { MESH_REG( kMeshTransferCount0, 0x01 ) }, // set MESH xfer count to 1
206: { MESH_REG( kMeshSequence, kMeshStatusCmd | kMeshSeqDMA )},// Status-in phase with DMA on
207: { STATUS_IN }, // input the status byte
208:
209: /* 0x420 - Message In: */
210:
211: { STAGE( kcclStageMsgI ) },
212: { CLEAR_CMD_DONE },
213: { MESH_REG( kMeshTransferCount1, 0x00 ) },
214: { MESH_REG( kMeshTransferCount0, 0x01 ) }, // set MESH xfer count to 1
215: { MESH_REG( kMeshSequence, kMeshMessageInCmd | kMeshSeqDMA )}, // Status-in phase with DMA on
216: { MSGI( 1 ) }, // get the Message-In byte
217:
218: /* 0x480 - Bus Free: */
219:
220: { STAGE( kcclStageFree ) },
221: { CLEAR_CMD_DONE },
222: { MESH_REG( kMeshSequence, kMeshEnableReselect ) }, // Enable Reselect
223: { MESH_REG( kMeshSequence, kMeshBusFreeCmd ) }, // Bus Free phase
224: { BR_IF_PROBLEM }, // branch if failed
225:
226: /* 0x4D0 kcclMESHintr - Good completion: */
227:
228: { STAGE( kcclStageGood ) },
229: { MESH_REG( kMeshInterruptMask, kMeshIntrMask ) }, // latch MESH interrupt
230: { STOP( kcclStageStop ) }, // Stop
231:
232: /* The rest of the Channel Program area is used for autosense */
233: /* and data transfer channel commands: */
234: /* kcclSenseBuffer Autosense area */
235: /* kcclDataXfer Start of data transfer channel commands */
236: /* kcclSenseResult Autosense result stored here */
237:
238: }; /* end gDescriptorList structure */
239:
240: const UInt32 gDescriptorListSize = sizeof( gDescriptorList );
241:
242: enum /* values for g.intLevel: */
243: {
244: kLevelISR = 0x80, /* In Interrupt Service Routine */
245: kLevelLocked = 0x40, /* MESH interrupts locked out */
246: kLevelSIH = 0x20, /* In Secondary Interrupt Handler */
247: kLevelLatched = 0x10 /* Interrupt latched */
248: };
249:
250:
251: // IONamedValue scsiChipRegisterStrings[] = { { 0, NULL, } };
252:
253: static int getConfigParam( id configTable, const char *paramName );
254: static unsigned int GetSCSICommandLength( const cdb_t *cdbPtr, unsigned int defaultLength );
255:
256:
257: /* MAX_DMA_XFER is set so that we don't have to worry about the */
258: /* ambiguous "zero" value in the MESH and DBDMA transfer registers */
259: /* that can mean either 65536 bytes or zero bytes. */
260:
261: #define MAX_DMA_XFER 0x0000F000
262:
263: #define ONE_SECOND 1 /* for IOScheduleFunc and serviceTimeoutInterrupt */
264:
265: /* Template for command message sent to the IO thread: */
266:
267: static const msg_header_t cmdMessageTemplate =
268: {
269: 0, /* msg_unused */
270: 1, /* msg_simple */
271: sizeof( msg_header_t ), /* msg_size */
272: MSG_TYPE_NORMAL, /* msg_type */
273: PORT_NULL, /* msg_local_port */
274: PORT_NULL, /* msg_remote_port - filled in */
275: IO_COMMAND_MSG /* msg_id */
276: };
277:
278: /* Template for timeout message. */
279:
280: static const msg_header_t gTimeoutMsgTemplate =
281: {
282: 0, /* msg_unused */
283: 1, /* msg_simple */
284: sizeof( msg_header_t ), /* msg_size */
285: MSG_TYPE_NORMAL, /* msg_type */
286: PORT_NULL, /* msg_local_port */
287: PORT_NULL, /* msg_remote_port - filled in */
288: IO_TIMEOUT_MSG /* msg_id */
289: };
290:
291: static port_t gKernelInterruptPort; /* for int/timeout msgs */
292:
293: static void serviceTimeoutInterrupt( void *arg );
294: static AppleMesh_SCSI *gInstance;
295:
296:
297: #if USE_ELG && !CustomMiniMon
298: void AllocateEventLog( UInt32 size )
299: {
300: if ( !g.evLogBuf ) g.evLogBuf = (UInt8*)kalloc( size );
301: if ( !g.evLogBuf )
302: kprintf( "probe - MESH evLog allocation failed " );
303:
304: g.evLogFlag = 0; /* assume insufficient memory */
305: g.evLogBufp = g.evLogBuf;
306:
307: if ( g.evLogBuf )
308: {
309: g.evLogBufe = g.evLogBufp + kEvLogSize - 0x20; // ??? overran buffer?
310: g.evLogFlag = 0xFEEDBEEF;
311: // g.evLogFlag = 0x0333;
312: }
313: return;
314: }/* end AllocateEventLog */
315:
316:
317: void EvLog( UInt32 a, UInt32 b, UInt32 ascii, char* str )
318: {
319: register UInt32 *lp; /* Long pointer */
320: ns_time_t time;
321:
322: if ( g.evLogFlag == 0 )
323: return;
324:
325: IOGetTimestamp( &time );
326:
327: lp = (UInt32*)g.evLogBufp;
328: g.evLogBufp += 0x10;
329:
330: if ( g.evLogBufp >= g.evLogBufe ) /* handle buffer wrap around if any */
331: { g.evLogBufp = g.evLogBuf;
332: if ( g.evLogFlag != 0xFEEDBEEF ) // make 0xFEEDBEEF a symbolic ???
333: g.evLogFlag = 0; /* stop tracing if wrap undesired */
334: }
335:
336: /* compose interrupt level with 3 byte time stamp: */
337:
338: *lp++ = (g.intLevel << 24) | ((time >> 10) & 0x003FFFFF); // ~ 1 microsec resolution
339: *lp++ = a;
340: *lp++ = b;
341: *lp = ascii;
342:
343: if( g.evLogFlag == 'step' )
344: kprintf( str );
345:
346: return;
347: }/* end EvLog */
348:
349:
350: void Pause( UInt32 a, UInt32 b, UInt32 ascii, char* str )
351: {
352: char work [256 ];
353: char name[] = "AppleMeshSCSI:";
354: char *bp = work;
355: UInt8 x;
356: int i;
357:
358:
359: EvLog( a, b, ascii, str );
360: EvLog( '****', '** P', 'ause', "*** Pause" );
361:
362: bcopy( name, bp, sizeof( name ) );
363: bp += sizeof( name ) - 1;
364:
365: *bp++ = '{'; // prepend p1 in hex:
366: for ( i = 7; i >= 0; --i )
367: {
368: x = a & 0x0F;
369: if ( x < 10 )
370: x += '0';
371: else x += 'A' - 10;
372: bp[ i ] = x;
373: a >>= 4;
374: }
375: bp += 8;
376:
377: *bp++ = ' '; // prepend p2 in hex:
378:
379: for ( i = 7; i >= 0; --i )
380: {
381: x = b & 0x0F;
382: if ( x < 10 )
383: x += '0';
384: else x += 'A' - 10;
385: bp[ i ] = x;
386: b >>= 4;
387: }
388: bp += 8;
389: *bp++ = '}';
390:
391: *bp++ = ' ';
392:
393: for ( i = sizeof( work ) - (int)(bp - work); i && (*bp++ = *str++); --i ) ;
394:
395: kprintf( work );
396: // call_kdp(); // ??? use kdp=3 in boot parameters
397: return;
398: }/* end Pause */
399: #endif /* not CustomMiniMon */
400:
401:
402: /* serviceTimeoutInterrupt - Handle timeouts. */
403: /* This function is invoked in kernel context on a DriverKit thread. */
404: /* Just send a timeout message to the IO thread to wake it up. */
405:
406: static void serviceTimeoutInterrupt( void *arg )
407: {
408: msg_header_t msg = gTimeoutMsgTemplate;
409:
410:
411: ELG( 0, 0, 'Tick', "serviceTimeoutInterrupt\n" );
412:
413: /* roll me another one: */
414: IOScheduleFunc( serviceTimeoutInterrupt, (void*)0x333, ONE_SECOND );
415:
416: /* Tell the IO thread: */
417: msg.msg_remote_port = gKernelInterruptPort;
418: msg_send_from_kernel( &msg, MSG_OPTION_NONE, 0 );
419: return;
420: }/* end serviceTimeoutInterrupt */
421:
422:
423: /* Used in timeoutOccurred to determine if specified cmdBuf has timed out. */
424: /* Returns YES if timeout, else NO. */
425:
426: static Boolean isCmdTimedOut( CommandBuffer *cmdBuf )
427: {
428: IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
429: ns_time_t now, expire;
430: Boolean result;
431:
432:
433: IOGetTimestamp( &now );
434: expire = cmdBuf->startTime +
435: (1000000000ULL * (unsigned long long)scsiReq->timeoutLength);
436: result = (now > expire);
437: if ( result ) ELG( cmdBuf, cmdBuf->scsiReq->timeoutLength, 'Tim-', "isCmdTimedOut" );
438: return result;
439: }/* end isCmdTimedOut */
440:
441:
442: /* Implement the public methods for the MESH controller. */
443:
444: @implementation AppleMesh_SCSI
445:
446: /* Create and initialize one instance of AppleMesh_SCSI. */
447: /* The work is done by architecture- and chip-specific modules. */
448:
449: + (Boolean) probe : deviceDescription
450: {
451: Boolean result;
452:
453:
454: gInstance = [ self alloc ]; /* Instantiate yourself */
455: g.intLevel = 0;
456:
457: MakeTimestampRecord( 512 ); /* conditionally compiled */
458:
459: #if USE_ELG
460: AllocateEventLog( kEvLogSize );
461: ELG( g.evLogBufp, &g.evLogFlag, 'Prob', "probe - event logging set up.\n" );
462: #endif /* USE_ELG */
463:
464: /* Perform device-specific initialization. */
465: /* Free the instance on failure. */
466:
467: if ( [ gInstance InitializeHardware : deviceDescription ] == nil )
468: result = NO;
469: else result = YES;
470:
471: return result;
472: }/* end probe */
473:
474:
475: /* The driver is shutting down. Kill everything worth killing. */
476:
477: - free
478: {
479: CommandBuffer cmdBuf;
480:
481:
482: /* First kill the IO thread if running. */
483:
484: if ( gFlagIOThreadRunning )
485: {
486: cmdBuf.op = kCommandAbortRequest;
487: cmdBuf.scsiReq = NULL;
488: [ self executeCmdBuf : &cmdBuf ];
489: }
490:
491: if ( incomingCmdLock )
492: [ incomingCmdLock free ];
493:
494: dbdma_stop( DBDMA_MESH_SCSI );
495:
496: if ( cclLogAddr )
497: {
498: IOFree( cclLogAddr, cclLogAddrSize );
499: cclLogAddr = NULL;
500: }
501: /* ??? Unmap physical address mapping to registers. */
502:
503: return [ super free ];
504: }/* end free */
505:
506:
507: /* Return required DMA alignment for current architecture. */
508: /* We specify 8-byte alignment to avoid a bug in the Grand Central chip:*/
509: /* if (Reading */
510: /* && (kdbdmaSetFlush || kdbdmaClrRun) */
511: /* && no bytes transferred yet */
512: /* && buffer not 8-byte aligned) */
513: /* { */
514: /* THEN memory in front of buffer will be trashed. */
515: /* } */
516:
517: - (void) getDMAAlignment : (IODMAAlignment*)alignment
518: {
519: alignment->readStart = DBDMA_ReadStartAlignment;
520: alignment->writeStart = DBDMA_WriteStartAlignment;
521: alignment->readLength = 0;
522: alignment->writeLength = 0;
523: return;
524: }/* end getDMAAlignment */
525:
526:
527: /* Statistics support. */
528:
529: - (unsigned int) numQueueSamples
530: {
531: return gTotalCommands;
532: }/* end numQueueSamples */
533:
534:
535: - (unsigned int) sumQueueLengths
536: {
537: return gQueueLenTotal;
538: }/* end sumQueueLengths */
539:
540:
541: - (unsigned int) maxQueueLength
542: {
543: return gMaxQueueLen;
544: }/* end maxQueueLength */
545:
546:
547: - (void) resetStatistics
548: {
549: gMaxQueueLen = 0;
550: gQueueLenTotal = 0;
551: gMaxQueueLen = 0;
552: return;
553: }/* resetStatistics */
554:
555:
556: /* Do a SCSI command, as specified by an IOSCSIRequest. */
557: /* All the work is done by the IO thread. */
558: /* @param scsiReq The request to execute */
559: /* @param buffer The data buffer to transfer to/from, if any */
560: /* @param client The data buffer owner task (for VM munging) */
561: /* */
562: /* This method is called from IOSCSIDevice */
563: - (sc_status_t) executeRequest : (IOSCSIRequest*)scsiReq
564: buffer : (void*)buffer
565: client : (vm_task_t)client
566: {
567: IOMemoryDescriptor *mem = NULL;
568: sc_status_t scsiStatus = SR_IOST_GOOD; /* Fool compiler */
569:
570:
571: ELG( scsiReq->lun<<16 | scsiReq->target, scsiReq, 'sReq', "executeRequest (buffer)" );
572: ELG( buffer, scsiReq->maxTransfer, 'Buff', "executeRequest" );
573:
574: /* Create a simple IO memory descriptor for this client, */
575: /* then toss it to the common method. */
576:
577: if ( buffer )
578: {
579: if ( scsiReq->read && ((UInt32)buffer & (DBDMA_ReadStartAlignment - 1)) )
580: {
581: ELG( scsiReq->maxTransfer, buffer, 'Aln-', "executeRequest/simple buffer - unaligned read buffer." );
582: return SR_IOST_ALIGN;
583: }
584: mem = [ [ IOSimpleMemoryDescriptor alloc ]
585: initWithAddress : (void*)buffer
586: length : scsiReq->maxTransfer ];
587: [ mem setClient : client ];
588: }
589:
590: scsiStatus = [ self executeRequest : scsiReq ioMemoryDescriptor : mem ];
591:
592: if ( mem )
593: [ mem release ];
594:
595: return scsiStatus;
596: }/* end executeRequest buffer */
597:
598:
599: /* Execute a SCSI request using an IOMemoryDescriptor. */
600: /* This allows callers to provide (kernel-resident) logical */
601: /* scatter-gather lists. For compatibility with existing */
602: /* implementations, the low-level SCSI device driver must */
603: /* first ensure that executeRequestWithIOMemoryDescriptor */
604: /* is supported by executing: */
605: /* [ controller respondsToSelector : executeRequestWithIOMemoryDescriptor ] */
606:
607: - (sc_status_t) executeRequest : (IOSCSIRequest*)scsiReq
608: ioMemoryDescriptor : (IOMemoryDescriptor*)ioMemoryDescriptor
609: {
610: CommandBuffer commandBuffer;
611:
612:
613: ELG( scsiReq->lun<<16 | scsiReq->target, scsiReq, 'dReq', "executeRequest (IOMemoryDescriptor)" );
614: ELG( 0, ioMemoryDescriptor, 'iomd', "executeRequest" );
615:
616: scsiReq->driverStatus = SR_IOST_INVALID; /* "In progress" */
617: if ( ioMemoryDescriptor )
618: {
619: [ ioMemoryDescriptor setMaxSegmentCount : MAX_DMA_XFER ];
620: [ ioMemoryDescriptor state : &commandBuffer.savedDataState ];
621: }
622: bzero( &commandBuffer, sizeof( CommandBuffer ) );
623: commandBuffer.op = kCommandExecute;
624: commandBuffer.scsiReq = scsiReq;
625: commandBuffer.mem = ioMemoryDescriptor;
626:
627: [ self executeCmdBuf : &commandBuffer ];
628:
629: #if TIMESTAMP_AT_IOCOMPLETE
630: [ self logTimestamp : "IO complete" ]; /* After RESULT macro */
631: #endif
632:
633: return commandBuffer.scsiReq->driverStatus;
634: }/* end executeRequest IOMemoryDescriptor */
635:
636:
637: /* Reset the SCSI bus. All the work is done by the IO thread. */
638:
639: - (sc_status_t) resetSCSIBus
640: {
641: CommandBuffer commandBuffer;
642:
643:
644: commandBuffer.op = kCommandResetBus;
645: commandBuffer.scsiReq = NULL;
646:
647: [ self executeCmdBuf : &commandBuffer ];
648: return SR_IOST_GOOD; /* can not fail */
649: }/* end resetSCSIBus */
650:
651:
652: /* The following 6 methods, */
653: /* interruptOccurred, interruptOccurredAt, otherOccurred, */
654: /* receiveMsg, timeoutOccurred, commandRequestOccurred, */
655: /* are all called from the IO thread in IODirectDevice. */
656:
657: /* Called from the IO thread when it receives an interrupt message. */
658: /* Currently all work is done by chip-specific module; maybe we should */
659: /* put this method there.... */
660:
661: - (void) interruptOccurred
662: {
663: g.intLevel |= kLevelISR; /* set ISR flag */
664: g.intLevel &= ~kLevelLatched; /* clear latched */
665: ELG( dbdmaAddr->d_status, dbdmaAddr->d_cmdptrlo, 'Int+', "interruptOccurred." );
666: // ELG( *(UInt32*)0xF3000024, *(UInt32*)0xF300002C, 'Int ', "interruptOccurred." );
667:
668: [ self DoHardwareInterrupt ]; /**** HANDLE THE INTERRUPT ****/
669:
670: // ELG( gActiveCommand, *(UInt32*)0xF300002C, 'Intx', "interruptOccurred." );
671:
672: g.intLevel &= ~kLevelISR; /* clear ISR flag */
673: return;
674: }/* end interruptOccurred */
675:
676:
677: /* These three should not occur; they are here as error traps. */
678: /* All three are called out from the IO thread upon receipt of */
679: /* messages which it should not be seeing. */
680:
681: - (void) interruptOccurredAt : (int)localNum
682: {
683: PAUSE( 0, localNum, 'int@', "interruptOccurredAt.\n" );
684: return;
685: }/* end interruptOccurredAt */
686:
687:
688: - (void) otherOccurred : (int)id
689: {
690: PAUSE( 0, id, 'Othr', "otherOccurred.\n" );
691: return;
692: }/* end otherOccurred */
693:
694:
695: - (void) receiveMsg
696: {
697: PAUSE( 0, 0, 'RcvM', "receiveMsg.\n" );
698:
699: /* We have to let IODirectDevice take care of this (i.e., */
700: /* dequeue the bogus message). */
701:
702: [ super receiveMsg ];
703: return;
704: }/* end receiveMsg */
705:
706:
707: /* This method is invoked by DriverKit when it receives a message */
708: /* generated by the function serviceTimeoutInterrupt() which was called */
709: /* by the kernel on some DriverKit thread. */
710:
711: - (void) timeoutOccurred
712: {
713: CommandBuffer *cmdBuf = gActiveCommand;
714: CommandBuffer *nextCmdBuf;
715:
716:
717: if ( g.intLevel & kLevelLatched )
718: {
719: ELG( cmdBuf, 0, 'TocL', "timeoutOccurred - interrupt already latched; do nothing" );
720: return;
721: }
722:
723: g.intLevel |= kLevelISR; /* set IOthread-running-flag */
724: ELG( CCLWord( kcclStageLabel ), dbdmaAddr->d_cmdptrlo, 'Tock', "timeoutOccurred - tick.\n" );
725: [ self GetHBARegsAndClear : FALSE ]; /* get the MESH registers */
726:
727: /* If gActiveCommand timed out: */
728:
729: if ( cmdBuf )
730: { if ( isCmdTimedOut( cmdBuf )
731: && ((CCLWord( kcclStageLabel ) != kcclStageFree)
732: || (CCLWord( kcclStageLabel ) != kcclStageGood) ) )
733: {
734: dbdma_flush( DBDMA_MESH_SCSI ); /* DBDMA may be hung in */
735: dbdma_stop( DBDMA_MESH_SCSI ); /* middle of transfer. */
736: // invalidate_cache_v( (vm_offset_t)cclLogAddr, cclLogAddrSize );
737:
738: cmdBuf->scsiReq->driverStatus = SR_IOST_IOTO;
739: [ self ioComplete : cmdBuf ];
740:
741: [ self AbortActiveCommand ];
742:
743: g.intLevel &= ~kLevelISR; /* clear IOthread-running-flag */
744: return;
745: }
746: }
747: else /* Move any/all timed-out disconnected commands to abortCmdQ: */
748: {
749: cmdBuf = (CommandBuffer*)queue_first( &disconnectedCmdQ );
750: while ( !queue_end( &disconnectedCmdQ, (queue_entry_t)cmdBuf ) )
751: {
752: nextCmdBuf = (CommandBuffer*)queue_next( &cmdBuf->link );
753: if ( isCmdTimedOut( cmdBuf ) )
754: { /* Move cmdBuf from disconnectQ to abortQ: */
755: queue_remove( &disconnectedCmdQ, cmdBuf, CommandBuffer*, link );
756: queue_enter( &abortCmdQ, cmdBuf, CommandBuffer*, link );
757: cmdBuf->scsiReq->driverStatus = SR_IOST_IOTO;
758: }
759: cmdBuf = nextCmdBuf;
760: }/* end WHILE scanning commands in the disconnected queue */
761: [ self AbortDisconnectedCommand ];
762: }
763: g.intLevel &= ~kLevelISR; /* clear IOthread-running-flag */
764: return;
765: }/* end timeoutOccurred */
766:
767:
768: /* Process all commands in incomingCmdQ. At most one of these */
769: /* will become gActiveCommand. The remainder of kCommandExecute commands*/
770: /* go to pendingCmdQ. Other types of commands (such as bus reset) */
771: /* are executed immediately. */
772: /* This method is called from IODirectDevice. */
773: /* */
774: /* Note that we don't have a concept of frozen queue. */
775:
776: - (void) commandRequestOccurred
777: {
778: CommandBuffer *cmdBuf, *pendCmd;
779:
780:
781: [ incomingCmdLock lock ];
782:
783: while ( !queue_empty( &incomingCmdQ ) )
784: {
785: cmdBuf = (CommandBuffer*)queue_first( &incomingCmdQ );
786: queue_remove( &incomingCmdQ, cmdBuf, CommandBuffer*, link );
787: [ incomingCmdLock unlock ];
788: ELG( gActiveCommand, cmdBuf, 'CRO+', "commandRequestOccurred" );
789:
790: switch ( cmdBuf->op )
791: {
792: case kCommandResetBus:
793: /* Note all active and disconnected commands will be terminated.*/
794: [ self threadResetBus : "Reset Command Received" ];
795: [ self ioComplete : cmdBuf ];
796: break;
797:
798: case kCommandAbortRequest:
799: /* 1. Abort all active, pending, and disconnected commands. */
800: /* 2. Notify caller of completion. */
801: /* 3. Self-terminate. */
802:
803: [ self abortAllCommands : SR_IOST_INT ];
804: pendCmd = (CommandBuffer*)queue_first( &pendingCmdQ );
805:
806: while ( !queue_end( &pendingCmdQ, (queue_entry_t)pendCmd ) )
807: {
808: pendCmd->scsiReq->driverStatus = SR_IOST_INT;
809: [ self ioComplete : pendCmd ];
810: pendCmd = (CommandBuffer*)queue_next( &pendCmd->link );
811: }
812:
813: [ cmdBuf->cmdLock lock ];
814: [ cmdBuf->cmdLock unlockWith : CMD_COMPLETE ];
815: IOExitThread();
816: /***** not reached *****/
817:
818: case kCommandExecute:
819: [ self threadExecuteRequest : cmdBuf ];
820: break;
821: }/* end SWITCH */
822:
823: [ incomingCmdLock lock ];
824: }/* end WHILE queue not empty */
825:
826: [ incomingCmdLock unlock ];
827: return;
828: }/* end commandRequestOccurred */
829:
830:
831: /* Power management methods. All we care about is power off, when */
832: /* we must reset the SCSI bus due to the Compaq BIOS's lack of a */
833: /* SCSI reset, which causes a hang if we have set up targets for */
834: /* sync data transfer mode. */
835:
836: - (IOReturn) getPowerState : (PMPowerState*)state_p
837: {
838: return IO_R_UNSUPPORTED;
839: }/* end getPowerState */
840:
841:
842: - (IOReturn) setPowerState : (PMPowerState) state
843: {
844: ELG( 0, state, 'sPwr', "setPowerState.\n" );
845:
846: if ( state == PM_OFF )
847: {
848: // [ self scsiReset ];
849: // ** ** ** TBS: [ self powerDown ];
850: return IO_R_SUCCESS;
851: }
852: return IO_R_UNSUPPORTED;
853: }/* end setPowerState */
854:
855:
856: - (IOReturn) getPowerManagement : (PMPowerManagementState*)state_p
857: {
858: return IO_R_UNSUPPORTED;
859: }/* end getPowerManagement */
860:
861:
862: - (IOReturn) setPowerManagement : (PMPowerManagementState)state
863: {
864: return IO_R_UNSUPPORTED;
865: }/* end setPowerManagement */
866:
867:
868: #if APPLE_MESH_ENABLE_GET_SET
869:
870: - (IOReturn) setIntValues : (unsigned*) parameterArray
871: forParameter : (IOParameterName) parameterName
872: count : (unsigned int) count
873: {
874: int target;
875: PerTargetData *perTargetPtr;
876: IOReturn ioReturn = IO_R_INVALID_ARG;
877:
878:
879: if ( strcmp( parameterName, APPLE_MESH_AUTOSENSE ) == 0 )
880: {
881: if ( count == 1 )
882: {
883: autoSenseEnable = parameterArray[0] ? 1 : 0;
884: ELG( 0, autoSenseEnable, 'sVas', "setIntValues - autoSense\n" );
885: ioReturn = IO_R_SUCCESS;
886: }
887: }
888: else if ( strcmp( parameterName, APPLE_MESH_CMD_QUEUE ) == 0 )
889: {
890: if ( count == 1 )
891: {
892: cmdQueueEnable = parameterArray[0] ? 1 : 0;
893: ELG( 0, cmdQueueEnable, 'sVqe', "setIntValues - cmdQueueEnable\n" );
894: ioReturn = IO_R_SUCCESS;
895: }
896: }
897: else if ( strcmp( parameterName, APPLE_MESH_SYNC ) == 0 )
898: {
899: if ( count == 1 )
900: {
901: syncModeEnable = parameterArray[0] ? 1 : 0;
902: ELG( 0, syncModeEnable, 'sVse', "setIntValues - syncModeEnable\n" );
903: ioReturn = IO_R_SUCCESS;
904: }
905: }
906: else if ( strcmp( parameterName, APPLE_MESH_FAST_SCSI ) == 0 )
907: {
908: if ( count == 1 )
909: {
910: fastModeEnable = parameterArray[0] ? 1 : 0;
911: ELG( 0, fastModeEnable, 'sVfe', "setIntValues - fastModeEnable\n" );
912: ioReturn = IO_R_SUCCESS;
913: }
914: }
915: else if ( strcmp( parameterName, APPLE_MESH_RESET_TARGETS ) == 0 )
916: {
917: if ( count == 0 )
918: {
919: /* Re-enable sync and command queuing. */
920: /* The disable bits persist after a reset. */
921: for ( target = 0; target < SCSI_NTARGETS; target++ )
922: {
923: perTargetPtr = &gPerTargetData[ target ];
924: perTargetPtr->syncDisable = FALSE;
925: perTargetPtr->maxQueue = 0;
926: perTargetPtr->inquiry_7 = 0;
927: }
928: ELG( 0, 0, 'sVrt', "setIntValues - reset targets\n" );
929: ioReturn = IO_R_SUCCESS;
930: }
931: }
932: else if ( strcmp( parameterName, APPLE_MESH_RESET_TIMESTAMP ) == 0 )
933: {
934: ResetTimestampIndex();
935: ioReturn = IO_R_SUCCESS;
936: }
937: else if ( strcmp( parameterName, APPLE_MESH_ENABLE_TIMESTAMP ) == 0 )
938: {
939: EnableTimestamp( TRUE );
940: ioReturn = IO_R_SUCCESS;
941: }
942: else if ( strcmp( parameterName, APPLE_MESH_DISABLE_TIMESTAMP ) == 0 )
943: {
944: EnableTimestamp( FALSE );
945: ioReturn = IO_R_SUCCESS;
946: }
947: else if ( strcmp( parameterName, APPLE_MESH_PRESERVE_FIRST_TIMESTAMP ) == 0 )
948: {
949: PreserveTimestamp( TRUE );
950: ioReturn = IO_R_SUCCESS;
951: }
952: else if ( strcmp( parameterName, APPLE_MESH_PRESERVE_LAST_TIMESTAMP ) == 0 )
953: {
954: PreserveTimestamp( FALSE );
955: ioReturn = IO_R_SUCCESS;
956: }
957: else
958: {
959: ioReturn [ super setIntValues : parameterArray
960: forParameter : parameterName
961: count : count ];
962: }
963: return ioReturn;
964: }/* end setIntValues */
965:
966:
967: - (IOReturn) getIntValues : (unsigned*) parameterArray
968: forParameter : (IOParameterName) parameterName
969: count : (unsigned*) count /* in/out */
970: {
971: IOReturn ioReturn = IO_R_INVALID_ARG;
972:
973:
974: if ( strcmp( parameterName, APPLE_MESH_AUTOSENSE) == 0 )
975: {
976: if ( *count == 1 )
977: {
978: parameterArray[0] = autoSenseEnable;
979: ioReturn = IO_R_SUCCESS;
980: }
981: }
982: else if ( strcmp( parameterName, APPLE_MESH_CMD_QUEUE ) == 0 )
983: {
984: if ( *count == 1 )
985: {
986: parameterArray[0] = cmdQueueEnable;
987: ioReturn = IO_R_SUCCESS;
988: }
989: }
990: else if ( strcmp( parameterName, APPLE_MESH_SYNC ) == 0 )
991: {
992: if ( *count == 1 )
993: {
994: parameterArray[0] = syncModeEnable;
995: ioReturn = IO_R_SUCCESS;
996: }
997: }
998: else if ( strcmp( parameterName, APPLE_MESH_FAST_SCSI ) == 0 )
999: {
1000: if ( *count == 1 )
1001: {
1002: parameterArray[0] = fastModeEnable;
1003: ioReturn = IO_R_SUCCESS;
1004: }
1005: }
1006: else if ( strcmp( parameterName, APPLE_MESH_RESET_TIMESTAMP ) == 0 )
1007: {
1008: ResetTimestampIndex();
1009: ioReturn = IO_R_SUCCESS;
1010: }
1011: else if ( strcmp( parameterName, APPLE_MESH_ENABLE_TIMESTAMP ) == 0 )
1012: {
1013: EnableTimestamp( TRUE );
1014: ioReturn = IO_R_SUCCESS;
1015: }
1016: else if ( strcmp( parameterName, APPLE_MESH_DISABLE_TIMESTAMP ) == 0 )
1017: {
1018: EnableTimestamp( FALSE );
1019: ioReturn = IO_R_SUCCESS;
1020: }
1021: else if ( strcmp( parameterName, APPLE_MESH_PRESERVE_FIRST_TIMESTAMP ) == 0 )
1022: {
1023: PreserveTimestamp( TRUE );
1024: ioReturn = IO_R_SUCCESS;
1025: }
1026: else if ( strcmp( parameterName, APPLE_MESH_PRESERVE_LAST_TIMESTAMP ) == 0 )
1027: {
1028: PreserveTimestamp( FALSE );
1029: ioReturn = IO_R_SUCCESS;
1030: }
1031: else
1032: {
1033: ioReturn = [ super getIntValues : parameterArray
1034: forParameter : parameterName
1035: count : count ];
1036: }
1037: return ioReturn;
1038: }/* end getIntValues */
1039:
1040: #endif APPLE_MESH_ENABLE_GET_SET
1041:
1042: @end /* AppleMesh_SCSI */
1043:
1044:
1045:
1046: @implementation AppleMesh_SCSI( Hardware )
1047:
1048: /* Perform MESH-specific initialization. */
1049: /* Fetch the device's bus address and interrupt port number. */
1050: /* Also, allocate one page of memory for the channel program. */
1051:
1052: - InitializeHardware : deviceDescription
1053: {
1054: IOReturn ioReturn = IO_R_SUCCESS;
1055: id result = self;
1056: kern_return_t kernelReturn;
1057: UInt8 target, lun;
1058: id configTable;
1059: const char *configValue;
1060: UInt8 deviceNumber;
1061: UInt8 functionNumber;
1062: UInt8 busNumber;
1063:
1064:
1065: configTable = [ deviceDescription configTable ];
1066: ASSERT( configTable );
1067: configValue = [ configTable valueForStringKey: "Bus Type" ];
1068:
1069: if ( configValue == NULL || strcmp( configValue, "PPC" ) )
1070: {
1071: PAUSE( 0, 'init', 'Hdw-', "InitializeHardware - bus type NG.\n" );
1072: ioReturn = IO_R_NO_DEVICE;
1073: }
1074:
1075: if ( ioReturn == IO_R_SUCCESS )
1076: {
1077: #if 0 // ** ** ** Need correct definition ** ** **
1078: ioReturn = [ deviceDescription getPCIDevice
1079: : &deviceNumber
1080: function : &functionNumber
1081: bus : &busNumber ];
1082: #else
1083: deviceNumber = 0;
1084: functionNumber = 0;
1085: busNumber = 0;
1086: kernelReturn = 0;
1087: #endif
1088: if ( ioReturn != IO_R_SUCCESS )
1089: PAUSE( 0, ioReturn, 'iHd-', "InitializeHardware - Can't get PCI device information.\n" );
1090: }
1091:
1092: if ( configValue )
1093: {
1094: [ configTable freeString : configValue ];
1095: configValue = NULL;
1096: }
1097:
1098: if ( ioReturn == IO_R_SUCCESS )
1099: ioReturn = [ self AllocHdwAndChanMem : deviceDescription ];
1100:
1101: if ( ioReturn == IO_R_SUCCESS )
1102: {
1103: for ( target = 0; target < SCSI_NTARGETS; target++ )
1104: {
1105: gPerTargetData[ target ].syncParms = kSyncParmsAsync;
1106: gPerTargetData[ target ].negotiateSDTR = kSyncParmsFast; // negotiate Fast
1107: gPerTargetData[ target ].inquiry_7 = 0;
1108: }
1109:
1110: /* All of the addresses are established. */
1111: /* Check that the hardware is present and working. */
1112: ioReturn = [ self DoHBASelfTest ];
1113: }
1114:
1115: if ( ioReturn == IO_R_SUCCESS )
1116: {
1117: /* Tell the superclass to initialize our IO thread. */
1118: /* After this, we should be able to execute SCSI requests. */
1119:
1120: if ( [ super initFromDeviceDescription : deviceDescription ] == NULL )
1121: {
1122: PAUSE( 0, 0, 'i h-', "InitializeHardware - Host Adaptor was not initialized. Fatal.\n" );
1123: ioReturn = IO_R_NO_DEVICE;
1124: }
1125: }
1126:
1127: if ( ioReturn == IO_R_SUCCESS )
1128: {
1129: gFlagIOThreadRunning = 1;
1130:
1131: /* Initialize local variables. Note that activeArray and */
1132: /* perTarget arrays are zeroed by objc runtime. */
1133:
1134: queue_init( &disconnectedCmdQ );
1135: queue_init( &incomingCmdQ );
1136: queue_init( &pendingCmdQ );
1137: queue_init( &abortCmdQ );
1138: incomingCmdLock = [ [ NXLock alloc ] init ];
1139: gActiveCommand = NULL;
1140: [ self resetStatistics ];
1141: gNextQueueTag = QUEUE_TAG_NONTAGGED + 1;
1142: gInitiatorID = kInitiatorIDDefault;
1143: gInitiatorIDMask = 1 << gInitiatorID; /* BusID bitmask for selection. */
1144: gFlagReselecting = FALSE;
1145:
1146: /* Reserve the initiator ID for all LUNs: */
1147:
1148: for ( lun = 0; lun < SCSI_NLUNS; lun++ )
1149: [ self reserveTarget : gInitiatorID lun : lun forOwner : self ];
1150:
1151: /* Get tagged command queueing, sync mode, */
1152: /* fast mode enables from configTable. */
1153:
1154: gOptionCmdQueueEnable = getConfigParam( configTable, CMD_QUEUE_ENABLE );
1155: gOptionSyncModeEnable = getConfigParam( configTable, SYNC_ENABLE );
1156: gOptionFastModeEnable = getConfigParam( configTable, FAST_ENABLE );
1157: gOptionExtendTiming = getConfigParam( configTable, EXTENDED_TIMING );
1158: gOptionAutoSenseEnable = AUTO_SENSE_ENABLE; // from bringup.h
1159:
1160: gOptionCmdQueueEnable = 1; /* Temp for testing??? */
1161:
1162: /* Get internal version of interruptPort; */
1163: /* set the port queue length to the maximum size. */
1164: /* It is not clear if we want to do this. */
1165:
1166: gKernelInterruptPort = IOConvertPort( [ self interruptPort ],
1167: IO_KernelIOTask,
1168: IO_Kernel );
1169: #if 0 /***** Need correct header file *****/
1170: kernelReturn = port_set_backlog( task_self(),
1171: [ self interruptPort ],
1172: PORT_BACKLOG_MAX );
1173: if ( kernelReturn != KERN_SUCCESS )
1174: PAUSE( 0, kernelReturn, 'i H-', "InitializeHardware - warning, port_set_backlog error.\n" );
1175: #endif
1176:
1177: /* Initialize the chip and reset the bus: */
1178:
1179: ioReturn = [ self ResetHardware : TRUE ];
1180: meshAddr->sourceID = gInitiatorID; // mlj ??? fix this
1181: }
1182:
1183: if ( ioReturn == IO_R_SUCCESS )
1184: {
1185: /* OK, we're ready to roll. */
1186:
1187: [ self enableInterrupt : 0 ];
1188: [ self registerDevice ];
1189:
1190: IOScheduleFunc( serviceTimeoutInterrupt, (void*)0x333, ONE_SECOND );
1191: }
1192: else
1193: { /* Do we need to free the locks and similar? */
1194: [ self free ];
1195: result = NULL;
1196: }
1197:
1198: return result;
1199: }/* end InitializeHardware */
1200:
1201:
1202: /* This includes a SCSI reset. */
1203: /* Handling of ioComplete of active and disconnected commands */
1204: /* must be done elsewhere. Returns IO_R_SUCCESS if successful. */
1205: /* This is called from a Task thread. It will disable and */
1206: /* re-enable interrupts. Reason is for error logging. */
1207:
1208: - (IOReturn) ResetHardware : (Boolean)resetSCSIBus
1209: {
1210: ELG( 0, resetSCSIBus, 'RstH', "ResetHardware - Bus Reset.\n" );
1211:
1212: [ self abortAllCommands : SR_IOST_RESET ];
1213: [ self ResetMESH : resetSCSIBus ];
1214:
1215: return IO_R_SUCCESS;
1216: }/* end ResetHardware */
1217:
1218:
1219: /* Start a SCSI transaction for the specified command. */
1220: /* ActiveCmd must be NULL. A return of kHardwareStartRejected */
1221: /* indicates that caller may try again with another command; */
1222: /* kHardwareStartBusy indicates a condition other than */
1223: /* (activeCmd != NULL) which prevents the processing of the command. */
1224:
1225: - (HardwareStartResult) hardwareStart : (CommandBuffer*) cmdBuf
1226: {
1227: IOSCSIRequest *scsiReq;
1228: HardwareStartResult result = kHardwareStartOK;
1229: cdb_t *cdbp;
1230: Boolean okToDisconnect = TRUE;
1231: Boolean okToQueue = gOptionCmdQueueEnable;
1232: UInt8 msgByte;
1233:
1234:
1235: ASSERT( cmdBuf && cmdBuf->scsiReq );
1236:
1237: scsiReq = cmdBuf->scsiReq;
1238: gCurrentTarget = scsiReq->target;
1239: gCurrentLUN = scsiReq->lun;
1240: cdbp = &scsiReq->cdb;
1241: gMsgOutFlag = 0;
1242:
1243: cmdBuf->cdbLength = GetSCSICommandLength( cdbp, scsiReq->cdbLength );
1244: if ( cmdBuf->cdbLength == 0 )
1245: {
1246: /* Failure: we can't determine the length of this command. */
1247:
1248: scsiReq->driverStatus = SR_IOST_CMDREJ;
1249: [ self ioComplete : cmdBuf ];
1250: result = kHardwareStartRejected;
1251: }
1252: { UInt8 *bp = (UInt8*)cdbp;
1253: ELG( ( bp[0]<<24) | (bp[1]<<16) | (bp[2]<<8) | bp[3],
1254: ( bp[4]<<24) | (bp[5]<<16) | (bp[6]<<8) | bp[7],
1255: '=CDB', "hardwareStart - CDB" );
1256: }
1257:
1258: if ( result == kHardwareStartOK )
1259: {
1260: /* Peek at the control byte (the last byte in the command). */
1261:
1262: msgByte = ((UInt8*)cdbp)[ cmdBuf->cdbLength - 1 ];
1263: if ( (msgByte & CTRL_LINKFLAG) != CTRL_NOLINK )
1264: {
1265: /* Failure: we don't support linked commands. */
1266:
1267: scsiReq->driverStatus = SR_IOST_CMDREJ;
1268: [ self ioComplete : cmdBuf ];
1269: result = kHardwareStartRejected;
1270: }
1271: }
1272:
1273: if ( result == kHardwareStartOK )
1274: {
1275: /* Autosense always renegotiates synchronous transfer mode. */
1276: /* This is necessary as the target might have been reset */
1277: /* or hit with a power-cycle. Autosense is never issued */
1278: /* with a queue tag. */
1279:
1280: cmdBuf->queueTag = QUEUE_TAG_NONTAGGED; /* No tag just yet */
1281: if ( cmdBuf->flagIsAutosense )
1282: {
1283: okToDisconnect = FALSE;
1284: gPerTargetData[ gCurrentTarget ].negotiateSDTR = gPerTargetData[ gCurrentTarget ].syncParms;
1285: }
1286: else
1287: {
1288: /* This is a real command. Setup the user data pointers */
1289: /* and counters and build a SCSI request CCL. */
1290: /* First, peek at the command for some special cases. */
1291:
1292: switch ( cdbp->cdb_opcode )
1293: {
1294: case kScsiCmdInquiry:
1295:
1296: /* The first command SCSIDisk sends us is an Inquiry. */
1297: /* This never gets retried, so avoid a possible */
1298: /* reject of a command queue tag. Avoid this hack if */
1299: /* there are any other commands outstanding for this */
1300: /* Target/LUN. */
1301:
1302: if ( gActiveArray[ scsiReq->target ][ scsiReq->lun ] == 0 )
1303: scsiReq->cmdQueueDisable = TRUE;
1304:
1305: okToDisconnect = FALSE; /* no disconnect, no queuing */
1306: break;
1307:
1308: case kScsiCmdRequestSense:
1309: /* Always force sync renegotiation on any Request Sense */
1310: /* to catch independent target power cycles. */
1311: /* (Sync renegotiation needed should be set after all */
1312: /* target-detected errors -- fix needed in MessageIn). */
1313: /* Sense is always issued with disconnect disabled to */
1314: /* maintain T/L/Q nexus. */
1315: /* Watch it: request sense from a client is incompatible*/
1316: /* with tagged queuing. */
1317:
1318: gPerTargetData[ gCurrentTarget ].negotiateSDTR
1319: = gPerTargetData[ gCurrentTarget ].syncParms;
1320: okToDisconnect = FALSE;
1321: break;
1322:
1323: case kScsiCmdTestUnitReady:
1324: case kScsiCmdReadCapacity:
1325: okToDisconnect = FALSE;
1326: break;
1327: }/* end SWITCH on opcode */
1328: }/* end ELSE not auto sense */
1329: }/* end IF kHardwareStartOK */
1330:
1331: okToDisconnect &= scsiReq->disconnect;
1332:
1333: okToQueue &= okToDisconnect
1334: && (scsiReq->cmdQueueDisable == FALSE)
1335: && (gPerTargetData[ scsiReq->target ].inquiry_7 & 0x02);
1336:
1337: cmdBuf->flagActive = 0; /* Initialize flags for this command. */
1338:
1339: /* Make sure that the HBA is stable before we */
1340: /* try to start a request. */
1341:
1342: if ( result == kHardwareStartOK )
1343: {
1344: if ( gActiveCommand )
1345: {
1346: /* This should never happen. It ensures that there are */
1347: /* no race conditions that reselect us between the time */
1348: /* threadExecuteRequest looked at gActiveCommand and */
1349: /* the time we disabled interrupts. */
1350:
1351: queue_enter( &pendingCmdQ, cmdBuf, CommandBuffer*, link );
1352: result = kHardwareStartBusy;
1353: }
1354: }
1355:
1356: if ( result == kHardwareStartOK )
1357: {
1358: /* Activate this command - if we fail later, we'll de-activate it. */
1359:
1360: ASSERT( gActiveCommand == NULL );
1361: [ self activateCommand : cmdBuf ];
1362: ASSERT( scsiReq->target == gCurrentTarget && scsiReq->lun == gCurrentLUN );
1363: [ self ClearCPResults ];
1364:
1365: /* Reset the message-out buffer pointer for the */
1366: /* Identify, SDTR, and queue tag messages. */
1367:
1368: msgOutPtr = (UInt8*)CCLAddress( kcclMSGOdata );
1369: msgByte = kScsiMsgIdentify | scsiReq->lun;
1370:
1371: if ( okToDisconnect )
1372: msgByte |= kScsiMsgEnableDisconnectMask;
1373:
1374: *msgOutPtr++ = msgByte;
1375:
1376: /* According to the SCSI Spec, the tag command */
1377: /* immediately follows the selection. */
1378: /* Note that autosense is never tagged.. */
1379: /* The command was initialized with QUEUE_TAG_NONTAGGED. */
1380: /***** Driver Kit only supports simple queue tags. *****/
1381:
1382: if ( okToQueue )
1383: {
1384: /* Avoid using tag QUEUE_TAG_NONTAGGED (zero). */
1385:
1386: cmdBuf->queueTag = gNextQueueTag;
1387: if ( ++gNextQueueTag == QUEUE_TAG_NONTAGGED )
1388: gNextQueueTag++;
1389: *msgOutPtr++ = kScsiMsgSimpleQueueTag;
1390: *msgOutPtr++ = cmdBuf->queueTag;
1391: gMsgOutFlag |= kFlagMsgOut_Queuing;
1392: }
1393:
1394: /* Do we need to negotiate SDTR for this target? */
1395:
1396: msgByte = gPerTargetData[ scsiReq->target ].negotiateSDTR;
1397: ELG( scsiReq->target << 16 | gPerTargetData[ scsiReq->target ].inquiry_7,
1398: gPerTargetData[ scsiReq->target ].syncParms << 16 | msgByte,
1399: 'SYN?', "Sync" );
1400: if ( !(gPerTargetData[ scsiReq->target ].inquiry_7 & 0x10) )
1401: {
1402: msgByte = 0; /* if Inquiry data doesn't permit Synchronous */
1403: }
1404: if ( msgByte )
1405: {
1406: // gPerTargetData[ scsiReq->target ].negotiateSDTR = 0;
1407: *msgOutPtr++ = kScsiMsgExtended; /* Extended Message */
1408: *msgOutPtr++ = 0x03; /* Message Length */
1409: *msgOutPtr++ = kScsiMsgSyncXferReq;
1410: if ( msgByte == kSyncParmsAsync )
1411: {
1412: *msgOutPtr++ = 200 >> 4; /* Period? used? */
1413: *msgOutPtr++ = 0; /* Offset (async) */
1414: }
1415: else
1416: {
1417: *msgOutPtr++ = 100 >> 2; /* 100 nSec period */
1418: *msgOutPtr++ = msgByte >> 4; /* FIFO size */
1419: }
1420: gMsgOutFlag |= kFlagMsgOut_SDTR;
1421: }/* end IF need to negotiate (a)sync */
1422:
1423: if ( cmdBuf->flagIsAutosense )
1424: {
1425: [ self InitAutosenseCCL ];
1426: }
1427: else
1428: {
1429: cmdBuf->currentDataIndex = 0;
1430: cmdBuf->savedDataIndex = 0;
1431: if ( cmdBuf->mem )
1432: {
1433: [ cmdBuf->mem setPosition : 0 ];
1434: [ cmdBuf->mem state : &cmdBuf->savedDataState ];
1435: }
1436: scsiReq->driverStatus = SR_IOST_INVALID;
1437: scsiReq->totalTime = 0;
1438: scsiReq->latentTime = 0;
1439: [ self UpdateCP : FALSE ]; /* Update the DBDMA Channel Program */
1440: }
1441:
1442: /***** Can a caller override the default timeout? *****/
1443:
1444: meshAddr->selectionTimeOut = gSelectionTimeout;
1445: meshAddr->destinationID = scsiReq->target;
1446: meshAddr->syncParms = gPerTargetData[ scsiReq->target ].syncParms;
1447: SynchronizeIO();
1448: [ self RunDBDMA : kcclStart stageLabel : kcclStageInit ];
1449: IOGetTimestamp( &cmdBuf->startTime );
1450: }
1451: return result;
1452: }/* end hardwareStart */
1453:
1454:
1455: @end /* AppleMesh_SCSI( Hardware ) */
1456:
1457:
1458: /* Obtain a YES/NO type parameter from the config table. */
1459: /* @param configTable The table to examine. */
1460: /* @param paramName The parameter to look for. */
1461: /* @result Zero if missing from the table or the table */
1462: /* value is not YES. One if present in the table and the */
1463: /* table value is YES. */
1464:
1465: static int getConfigParam( id configTable, const char *paramName )
1466: {
1467: const char *value;
1468: int rtn = 0; // default if not present in table
1469:
1470:
1471: value = [ configTable valueForStringKey : paramName ];
1472: if ( value )
1473: {
1474: if ( strcmp( value, "YES" ) == 0 )
1475: rtn = 1;
1476: [ configTable freeString : value ];
1477: }
1478: return rtn;
1479: }/* end getConfigParam */
1480:
1481:
1482: static unsigned int GetSCSICommandLength( const cdb_t *cdbPtr, unsigned int defaultLength )
1483: {
1484: unsigned int result;
1485:
1486: /* Warning: don't use sizeof here - the compiler rounds */
1487: /* the value up to the next word boundary. */
1488:
1489: switch ( ((UInt8*)cdbPtr)[0] & 0xE0 )
1490: {
1491: case (0 << 5): result = 6; break;
1492: case (1 << 5):
1493: case (2 << 5): result = 10; break;
1494: case (5 << 5): result = 12; break;
1495: case (6 << 5): result = (defaultLength != 0) ? defaultLength : 6; break;
1496: case (7 << 5): result = (defaultLength != 0) ? defaultLength : 10; break;
1497: default: result = 0; break;
1498: }
1499: return result;
1500: }/* end GetSCSICommandLength */
1501:
1502:
1503: /* These are the hardware-specific methods that are not */
1504: /* explicitly tied to Mesh and DBDMA. */
1505:
1506: @implementation AppleMesh_SCSI( HardwarePrivate )
1507:
1508: /* Fetch the device's bus address and allocate one page of memory */
1509: /* for the channel command. (Strictly speaking, we don't need an */
1510: /* entire page, but we can use the rest of the page for a permanent */
1511: /* status log). */
1512: /* @param deviceDescription Specify the device to initialize. */
1513: /* @return IO_R_SUCCESS if successful, else an error status. */
1514:
1515: - (IOReturn) AllocHdwAndChanMem : deviceDescription
1516: {
1517: IOReturn ioReturn = IO_R_SUCCESS;
1518: enum
1519: { kMESHRegisterBase = 0,
1520: kDBDMARegisterBase = 1,
1521: kNumberRegisters = 2
1522: };
1523:
1524:
1525: meshAddr = (MeshRegister*)gMESHPhysAddr = 0;
1526: dbdmaAddr = (dbdma_regmap_t*)dbdmaAddrPhys = 0;
1527:
1528: /* Set the default selection timeout to the MESH value (10 msec units). */
1529:
1530: gSelectionTimeout = 250 / 10; // ??? symbolic
1531:
1532: /* Allocate a page of wired-down memory in the kernel. Although */
1533: /* Driver Kit provides a memory allocator, IOMalloc, it does */
1534: /* not guarantee page alignment. Thus, we call the Mach kernel */
1535: /* routine. According to the description of kalloc(), 8192 is */
1536: /* the smallest amount of memory we can allocate. The channel */
1537: /* command area will fit into the start of this area. */
1538:
1539: cclLogAddrSize = page_size;
1540: cclLogAddr = (DBDMADescriptor*)kalloc( cclLogAddrSize );
1541: if ( !cclLogAddr )
1542: { PAUSE( 0, cclLogAddrSize, 'CCA-', "AllocHdwAndChanMem - can't allocate channel command area.\n" );
1543: ioReturn = IO_R_NO_MEMORY;
1544: }
1545:
1546: if ( ioReturn == IO_R_SUCCESS )
1547: {
1548: if ( IOIsAligned( cclLogAddr, page_size ) == 0 )
1549: {
1550: PAUSE( 0, cclLogAddr, 'cca-', "AllocHdwAndChanMem - not page-aligned.\n" );
1551: ioReturn = IO_R_NO_MEMORY;
1552: }
1553: }
1554:
1555: if ( ioReturn == IO_R_SUCCESS )
1556: {
1557: /* Remember the number of DBDMA descriptors that */
1558: /* can be used for data transfer channel commands. */
1559:
1560: gDBDMADescriptorMax = (cclLogAddrSize - kcclDataXfer)
1561: / sizeof( DBDMADescriptor );
1562: #if 0
1563: /* Fetch the logical and physical addresses */
1564: /* to access the MESH and DBDMA hardware. */
1565:
1566: memoryRangeList = [ deviceDescription memoryRangeList ];
1567: numMemoryRanges = [ deviceDescription numMemoryRanges ];
1568: for ( i = 0; i < numMemoryRanges; i++ )
1569: ELG( memoryRangeList[ i ].start, memoryRangeList[ i ].size, 'Rang', "AllocHdwAndChanMem - range start & size.\n" );
1570: if ( numMemoryRanges != kNumberRegisters )
1571: { PAUSE( memoryRangeList[ i ].start, memoryRangeList[ i ].size, 'Rng-', "AllocHdwAndChanMem - numMemoryRanges != kNumberRegisters.\n" );
1572: ioReturn = IO_R_INVALID; /* This "can't happen" */
1573: }
1574: #endif
1575: }
1576:
1577: #if 0
1578: if ( ioReturn == IO_R_SUCCESS )
1579: {
1580: /* We know that the first range describes the MESH chip, */
1581: /* and the second range describes the DBDMA chip. */
1582:
1583: gMESHPhysAddr = (PhysicalAddress)memoryRangeList[ kMESHRegisterBase ].start;
1584: dbdmaAddrPhys = (PhysicalAddress)memoryRangeList[ kDBDMARegisterBase ].start;
1585:
1586: /* Weave together the logical and physical addresses. */
1587: /* First, map the MESH and DBDMA chips into our address space. */
1588:
1589: ioReturn = IOMapPhysicalIntoIOTask( (UInt32)gMESHPhysAddr,
1590: sizeof( MeshRegister ),
1591: (vm_address_t*)&meshAddr );
1592: if ( ioReturn != IO_R_SUCCESS )
1593: PAUSE( 0, ioReturn, 'map-', "AllocHdwAndChanMem - MESH mapping err.\n" );
1594: }
1595:
1596: if ( ioReturn == IO_R_SUCCESS )
1597: {
1598: ioReturn = IOMapPhysicalIntoIOTask( (UInt32)dbdmaAddrPhys,
1599: sizeof( dbdma_regmap_t ),
1600: (vm_address_t*)&dbdmaAddr );
1601: if ( ioReturn != IO_R_SUCCESS )
1602: PAUSE( 0, ioReturn, 'Map-', "AllocHdwAndChanMem - DBDMA mapping err.\n" );
1603: }
1604: #else
1605: if ( ioReturn == IO_R_SUCCESS )
1606: {
1607: meshAddr = (MeshRegister*)gMESHPhysAddr = (PhysicalAddress)PCI_MESH_BASE_PHYS;
1608:
1609: // dbdmaAddr = (dbdma_regmap_t*)PCI_MESH_DMA_BASE_PHYS;
1610: dbdmaAddr = (dbdma_regmap_t*)DBDMA_REGMAP( DBDMA_MESH_SCSI );
1611: // dbdmaAddrPhys = (PhysicalAddress)KVTOPHYS( (vm_offset_t)dbdmaAddr );
1612: dbdmaAddrPhys = (PhysicalAddress)dbdmaAddr;
1613:
1614: ELG( dbdmaAddrPhys, dbdmaAddr, 'DBDM',
1615: "AllocHdwAndChanMem - DBDMA phys/logical addresses." );
1616: g.meshAddr = (UInt32)meshAddr; // for debugging, miniMon ...
1617: #if CustomMiniMon
1618: gMESH_DBDMA = (UInt32)dbdmaAddr;
1619: gMESH_DBDMA_Phys = (UInt32)dbdmaAddrPhys;
1620: #endif /* CustomMiniMon */
1621: }
1622:
1623: #endif
1624:
1625: if ( ioReturn == IO_R_SUCCESS )
1626: {
1627: /* Ensure that the addresses are valid: */
1628:
1629: ASSERT( probe_rb( meshAddr ) == 0 );
1630: ASSERT( probe_rb( dbdmaAddr ) == 0 );
1631:
1632: /* Get the physical address corresponding the DBDMA channel area: */
1633:
1634: ioReturn = IOPhysicalFromVirtual( IOVmTaskSelf(),
1635: (UInt32)cclLogAddr,
1636: (vm_offset_t*)&cclPhysAddr );
1637: g.cclPhysAddr = (UInt32)cclPhysAddr; // for debugging ease
1638: g.cclLogAddr = (UInt32)cclLogAddr;
1639: if ( ioReturn != IO_R_SUCCESS )
1640: PAUSE( 0, ioReturn, 'MAP-', "AllocHdwAndChanMem - DBDMA mapping err.\n" );
1641: }
1642:
1643: if ( ioReturn == IO_R_SUCCESS)
1644: {
1645: ELG( cclPhysAddr, cclLogAddr, '=CCL',
1646: "AllocHdwAndChanMem - CCL phys/logical addresses." );
1647: [ self InitCP ];
1648: }
1649: /* What do we do on failure? Should we try to deallocate */
1650: /* the stuff we created, or will the system do this for us? */
1651:
1652: return ioReturn;
1653: }/* end AllocHdwAndChanMem */
1654:
1655:
1656: /* Perform one-time-only channel command program initialization. */
1657:
1658: - (void) InitCP
1659: {
1660: register DBDMADescriptor *dst = cclLogAddr;
1661: register const DBDMADescriptor *src = gDescriptorList;
1662: UInt32 i;
1663: UInt8 *bp;
1664:
1665:
1666:
1667: /* Set the interrupt, branch, and wait DBDMA registers. */
1668: /* Caution: the following MESH interrupt register bits are */
1669: /* EndianSwapped, reverse polarity and in a different position. */
1670: /* The pattern is: 0xvv00mm00, where mm is a mask byte */
1671: /* and vv is a value byte to match. (After EndianSwapping). */
1672: /* 0x80 means NO errors (kMeshIntrError) */
1673: /* 0x40 means NO exceptions (kMeshIntrException) */
1674: /* 0x20 means NO command done (kMeshIntrCmdDone) */
1675: /* Branch Select is used with BRANCH_FALSE */
1676:
1677: // DBDMASetInterruptSelect( 0x00000000 ); /* Never let DBDMA interrupt */
1678: // DBDMASetWaitSelect( 0x00200020 ); /* Wait until command done */
1679: // DBDMASetBranchSelect( 0x00C000C0 ); /* Branch if exception or error */
1680:
1681: *(volatile UInt32*)&dbdmaAddr->d_intselect = 0x00000000; /* Never let DBDMA interrupt */
1682: *(volatile UInt32*)&dbdmaAddr->d_wait = 0x20002000; /* Wait until command done */
1683: *(volatile UInt32*)&dbdmaAddr->d_branch = 0xC000C000; /* Br if Exc or Err */
1684: SynchronizeIO();
1685:
1686: /* Relocate and EndianSwap the global channel command list */
1687: /* into the page that is shared with the DBDMA device. */
1688:
1689: for ( i = 0; i < gDescriptorListSize; i += sizeof( DBDMADescriptor ) )
1690: {
1691: dst->operation = SWAP( src->operation ); /* copy command with count */
1692:
1693: switch ( src->result & kRelAddress )
1694: {
1695: case kRelAddressMESH:
1696: dst->address = SWAP( src->address + (UInt32)gMESHPhysAddr );
1697: break;
1698: case kRelAddressCP:
1699: dst->address = SWAP( src->address + (UInt32)cclPhysAddr );
1700: break;
1701: case kRelAddressPhys:
1702: dst->address = SWAP( src->address );
1703: break;
1704: default:
1705: dst->address = SWAP( src->address );
1706: break;
1707: }
1708:
1709: switch ( src->result & kRelCmdDep )
1710: {
1711: case kRelCmdDepCP:
1712: dst->cmdDep = SWAP( src->cmdDep + (UInt32)cclPhysAddr );
1713: break;
1714: case kRelCmdDepLabel:
1715: dst->cmdDep = src->cmdDep;
1716: break;
1717: default:
1718: dst->cmdDep = SWAP( src->cmdDep );
1719: break;
1720: }
1721:
1722: dst->result = 0;
1723: src++;
1724: dst++;
1725: } /* FOR all elements in the descriptor list */
1726:
1727: /* Build a SCSI CDB for the autosense Request Sense command. */
1728:
1729: bp = (UInt8*)CCLAddress( kcclSenseCDB );
1730: *bp++ = kScsiCmdRequestSense; /* Command */
1731: *bp++ = 0; /* LUN to be filled in */
1732: *bp++ = 0; /* reserved */
1733: *bp++ = 0; /* reserved */
1734: *bp++ = kMaxAutosenseByteCount; /* Allocation length - to be filled in */
1735: *bp++ = 0; /* Control (flag) */
1736: return;
1737: }/* end InitCP */
1738:
1739:
1740: /* Initialize the data transfer channel command list for a normal SCSI */
1741: /* command. The channel command list has a complex structure of */
1742: /* transfer groups and items, where: */
1743: /* transfer group The number of bytes transferred by a single */
1744: /* MESH operation. This will be from 1 to */
1745: /* kMaxDMATransferLength (65536 - 4096). */
1746: /* transfer item The number of bytes transferred by a single */
1747: /* DBDMA operation. These bytes are guaranteed */
1748: /* to be physically-contiguous. */
1749: /* Thus, the data transfer CCL looks like the following: */
1750: /* Prolog 1: Load MESH with the first group count. */
1751: /* Item 1.1: Load DBDMA with the first physical address and */
1752: /* item count. */
1753: /* Item 1.2 etc: Load DBDMA with the next physical address and */
1754: /* item count. */
1755: /* Prolog 2, etc. Load MESH with the next group count. */
1756: /* Item 2.1, etc. Load DBDMA with the next group of physical */
1757: /* addresses. */
1758: /* Stop/Branch If all of the data transfer commands fit in the */
1759: /* channel command list, branch to the Status phase*/
1760: /* channel command. Otherwise, stop transfer */
1761: /* (which stops in Data phase) and re-build the */
1762: /* command list for the next set of data. */
1763: /* Note that the last DBDMA command must be INPUT_LAST or OUTPUT_LAST */
1764: /* to handle synchronous transfer odd-byte disconnect. */
1765:
1766: - (void) UpdateCP : (Boolean) reselecting
1767: {
1768: CommandBuffer *cmdBuf;
1769: IOSCSIRequest *scsiReq;
1770: DBDMADescriptor *descProto = CCLDescriptor( kcclPrototype );
1771: IOReturn ioReturn = IO_R_SUCCESS;
1772: DBDMADescriptor *descriptorPtr; /* current data descriptor */
1773: DBDMADescriptor *descriptorMax; /* beyond the last data descriptor */
1774: DBDMADescriptor *preamblePtr; /* current prolog descriptor */
1775: UInt32 dbdmaOpProto; /* prototype Opcode for DBDMA */
1776: UInt32 dbdmaOp; /* Opcode for DBDMA */
1777: UInt32 meshSeq; /* Opcode for MESH request */
1778: SInt32 transferLength; /* Number of bytes left to transfer */
1779: UInt32 totalXferLen = 0; /* Total length of this transfer */
1780: UInt32 groupLength; /* Number of bytes in this group */
1781: UInt8 syncParms; /* Fast synchronous param value */
1782: ByteCount bc;
1783: PhysicalRange range;
1784: ItemCount rangeByteCount;
1785: DBDMADescriptor *dp;
1786:
1787:
1788: ASSERT( gActiveCommand && gActiveCommand->scsiReq );
1789: cmdBuf = gActiveCommand;
1790: scsiReq = cmdBuf->scsiReq;
1791: ASSERT( scsiReq->target == gCurrentTarget && scsiReq->lun == gCurrentLUN );
1792:
1793: /* How many descriptors can we store (need some slop for the */
1794: /* terminator commands). Get a pointer to the first free */
1795: /* descriptor and the total number of bytes left to transfer in */
1796: /* this IO request. */
1797:
1798: descriptorPtr = CCLDescriptor( kcclDataXfer );
1799: descriptorMax = &descriptorPtr[ gDBDMADescriptorMax - 16 ];
1800: transferLength = scsiReq->maxTransfer - cmdBuf->currentDataIndex;
1801: ELG( cmdBuf, transferLength, 'UpCP', "UpdateCP" );
1802:
1803: if ( reselecting == FALSE )
1804: {
1805: [ self SetupMsgO ]; /* Setup for Message Out phase. */
1806:
1807: /* Setup for Command phase: */
1808: CCLByte( kcclCmdoMTC ) = cmdBuf->cdbLength; /* MESH transfer count */
1809: CCLByte( kcclCmdoDTC ) = cmdBuf->cdbLength; /* DBDMA count */
1810: bcopy( &scsiReq->cdb, CCLAddress( kcclCMDOdata ), cmdBuf->cdbLength );
1811: }
1812:
1813: /* Generate MESH "sequence" & DBDMA "operation" for Input or Output: */
1814:
1815: if ( scsiReq->read )
1816: { dbdmaOpProto = INPUT_MORE | kBranchIfFalse;
1817: meshSeq = kMeshDataInCmd | kMeshSeqDMA;
1818: }
1819: else
1820: { dbdmaOpProto = OUTPUT_MORE | kBranchIfFalse;
1821: meshSeq = kMeshDataOutCmd | kMeshSeqDMA;
1822: }
1823:
1824: CCLWord( kcclBatchSize ) = 0;
1825:
1826: while ( ioReturn == IO_R_SUCCESS
1827: && transferLength > 0
1828: && descriptorPtr < descriptorMax )
1829: {
1830: /* Do one group, ie, enough CCs to fill a MESH transfer count. */
1831: /* There are more data to be transferred, and CCL space to store*/
1832: /* another group of data. First, leave space for the preamble. */
1833:
1834: preamblePtr = descriptorPtr;
1835: groupLength = 0;
1836: descriptorPtr += 4; /* Preamble takes 4 descriptors */
1837:
1838: while ( transferLength > 0 /* more to xfer */
1839: && descriptorPtr < descriptorMax ) /* room in CCL */
1840: {
1841: /* Do one physically contiguous segment: */
1842:
1843: bc = MAX_DMA_XFER - groupLength; /* calc room left in group */
1844: if ( bc < page_size )
1845: break;
1846: rangeByteCount = [ cmdBuf->mem getPhysicalRanges : (ItemCount) 1
1847: maxByteCount : bc
1848: newPosition : NULL
1849: actualRanges : NULL
1850: physicalRanges : &range ];
1851:
1852: if ( rangeByteCount == 0 )
1853: break;
1854:
1855: ASSERT( range.length > 0 );
1856: groupLength += range.length;
1857: transferLength -= range.length;
1858: dbdmaOp = dbdmaOpProto | range.length;
1859: if ( transferLength <= 0 )
1860: dbdmaOp |= (OUTPUT_MORE ^ OUTPUT_LAST); /* add LAST to cmd */
1861: descriptorPtr->operation = SWAP( dbdmaOp );
1862: descriptorPtr->address = SWAP( (UInt32)range.address );
1863: descriptorPtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclProblem );
1864: descriptorPtr->result = 0; // for debugging
1865: descriptorPtr++;
1866: }/* end inner WHILE */
1867:
1868: if ( groupLength == 0 )
1869: {
1870: /* Nothing was built - we apparently failed to get */
1871: /* a physical address. Note: there is a potential problem with */
1872: /* the following sequence as the *previous* DBDMA command, if */
1873: /* any, should be changed to set xxPUT_LAST. */
1874:
1875: ELG( 0, 0, 'Grp-', "UpdateCP - groupLength is 0" );
1876: preamblePtr->operation = SWAP(
1877: NOP_CMD | kBranchIfFalse | kWaitIfTrue );
1878: preamblePtr->address = 0;
1879: preamblePtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclProblem );
1880: preamblePtr->result = 0;
1881: descriptorPtr = preamblePtr + 1;
1882: ioReturn = IO_R_INVALID; /* Exit the outer loop */
1883: }
1884: else
1885: {
1886: totalXferLen += groupLength;
1887:
1888: /* This group is complete. Fill in the preamble. */
1889: /* The preamble consists of the following commands: */
1890: /* [0] Move <totalXferLen> to kcclBatchSize */
1891: /* [1] Store group length high-byte in MESH */
1892: /* transfer count 1 register */
1893: /* [2] Store group length low-byte in MESH */
1894: /* transfer count 1 register */
1895: /* [3] Store the input/output command in the MESH */
1896: /* sequence register. */
1897: /* If the command finishes prematurely (perhaps the */
1898: /* device wants to disconnect), the interrupt service */
1899: /* routine will use totalXferLen - the residual byte */
1900: /* count to determine the number of bytes xferred. */
1901:
1902: descProto[0].cmdDep = totalXferLen; // update batch size
1903: descProto[1].cmdDep = SWAP( groupLength >> 8 );
1904: descProto[2].cmdDep = SWAP( groupLength & 0xFF );
1905: descProto[3].cmdDep = SWAP( meshSeq );
1906: bcopy( descProto, preamblePtr, sizeof( DBDMADescriptor ) * 4 );
1907: ELG( preamblePtr, totalXferLen, '=Tot', "UpdateCP - set preamble" );
1908:
1909: /* If there is another group, wait for */
1910: /* cmdDone and clear it: */
1911: if ( transferLength > 0 )
1912: { /* Wait for CmdDone: */
1913: bcopy( CCLDescriptor( kcclBrProblem ), descriptorPtr, sizeof( DBDMADescriptor ) );
1914: ++descriptorPtr;
1915: /* Clear CmdDone: */
1916: /* HACK - if we reached the end of the CCL page, */
1917: /* we don't want to clear cmdDone because we will lose */
1918: /* an interrupt. So, this instruction may be deleted */
1919: /* down below. (Radar 2298440) */
1920: descriptorPtr->operation = SWAP( STORE_QUAD | KEY_SYSTEM | 1 );
1921: descriptorPtr->address = SWAP( (UInt32)gMESHPhysAddr + kMeshInterrupt );
1922: descriptorPtr->cmdDep = SWAP( kMeshIntrCmdDone );
1923: descriptorPtr->result = 0;
1924: ++descriptorPtr;
1925: }/* end IF not last group */
1926: }/* end if/ELSE a group was built */
1927: }/* end outer WHILE */
1928:
1929: /* All of the data have been transferred (or we ran off the end */
1930: /* of the CCL). Update the transfer start index to reflect on */
1931: /* what we *think* we will transfer in this DATA operation. If */
1932: /* we completed DATA phase, branch to the Status Phase CCL; */
1933: /* if not, stop the channel command so we can reload the CCL */
1934: /* with the next big chunk. */
1935: /* When the transfer completes, the last prolog will have stored*/
1936: /* the total number of bytes transferred in a known location in */
1937: /* the CCL area. */
1938: /* Now, append the data transfer postamble to handle */
1939: /* synchronous odd-byte disconnect and jump to status phase */
1940: /* (or just stop if there's more DMA) */
1941:
1942: #define kMaxPostamble 9
1943: #define kDBDMADescriptorEnd (CCLDescriptor(kcclDataXfer) + gDBDMADescriptorMax)
1944:
1945: ASSERT( descriptorPtr + kMaxPostamble < kDBDMADescriptorEnd );
1946:
1947: /* Do some synchronous data transfer cleanup: */
1948:
1949: syncParms = gPerTargetData[ scsiReq->target ].syncParms;
1950: meshAddr->syncParms = syncParms;
1951: SynchronizeIO();
1952: ELG( gMsgOutFlag, syncParms, 'SynP', "UpdateCP - sync parms" );
1953:
1954: if ( ((syncParms & 0xF0) || (gMsgOutFlag & kFlagMsgOut_SDTR)) // Sync?
1955: && (totalXferLen > 0) // any data moving?
1956: && (transferLength == 0) ) // end of xfer?
1957: {
1958: gFlagIncompleteDBDMA = FALSE; /* indicate complete xfer */
1959:
1960: /* MESH has a problem at the end of Synchronous transfers. */
1961: /* If the target is fast enough, it can move from data phase to */
1962: /* Status phase while MESH still has ACKed bytes in its FIFO and */
1963: /* the DBDMA is still running. MESH raises PhaseMismatch Exception */
1964: /* causing an interrupt in which we must empty the FIFO and move */
1965: /* the bytes to the user's buffer by programmed IO. */
1966: /* If the target is not fast enough, we can save the interrupt and */
1967: /* bypass the mess. */
1968: /* So, we do the following: */
1969: /* 1) Enable only MESH Err interrupts; disable Exc and CmdDone. */
1970: /* 2) Don't Wait; Branch if an interrupt may have already occurred.*/
1971: /* 3) Wait for cmdDone at least for TC = FIFO count = 0 and */
1972: /* maybe including PhaseMismatch. Branch to SyncCleanup if PMM. */
1973: /* 4) Assume an interphase condition as opposed to an */
1974: /* overrun condition and Branch Always to get Status. */
1975:
1976: /* If the Channel Program gets this far, the OUTPUT_LAST */
1977: /* has finished writing its data to the FIFO and MESH may still */
1978: /* be putting bytes on the bus OR the INPUT_LAST has read all */
1979: /* its data from the FIFO and MESH has already ACKed them. */
1980: /* There may be or not some time before REQ appears again, */
1981: /* either for data overrun or the next phase. */
1982:
1983: /* Disable Exc and CmdDone (leave Err enabled): */
1984:
1985: descriptorPtr->operation = SWAP( STORE_QUAD | KEY_SYSTEM | 1 );
1986: descriptorPtr->address = SWAP( (UInt32)gMESHPhysAddr + kMeshInterruptMask );
1987: descriptorPtr->cmdDep = SWAP( kMeshIntrError );
1988: descriptorPtr->result = 0;
1989: ++descriptorPtr;
1990:
1991: /* Take the interrupt if PhaseMismatch not definitely caught. */
1992: /* Branch (don't wait for cmdDone) if Exc may have already occurred: */
1993:
1994: descriptorPtr->operation = SWAP( NOP_CMD | kBranchIfFalse );
1995: descriptorPtr->address = 0;
1996: descriptorPtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclProblem );
1997: descriptorPtr->result = 0;
1998: /* Radar 2281306 ( and 2272931 ): */
1999: /* Output may completely fit in the FIFO and not make it out */
2000: /* to the SCSI bus if the target disconnects after the command. */
2001: /* If that's possible, wait here for cmdDone and */
2002: /* take the PhaseMismatch interrupt. This situation occurred on a */
2003: /* Mode Select with an output of 12 bytes. Do this to prevent */
2004: /* the Stage from advancing from kcclStageXfer so that proper */
2005: /* cleanup can take place. */
2006: if ( (totalXferLen < 16) && !scsiReq->read )
2007: descriptorPtr->operation= SWAP( NOP_CMD | kWaitIfTrue | kBranchIfFalse );
2008: ++descriptorPtr;
2009:
2010: /* Possible PhaseMisMatch caught after FIFO emptied. */
2011: /* Wait for cmdDone. If Exc, branch to SyncCleanUp: */
2012:
2013: descriptorPtr->operation = SWAP( NOP_CMD | kWaitIfTrue | kBranchIfFalse );
2014: descriptorPtr->address = 0;
2015: descriptorPtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclSyncCleanUp );
2016: descriptorPtr->result = 0;
2017: descriptorPtr++;
2018:
2019: /* Interphase condition or possible overrun. */
2020: /* 29sep98 PhaseMismatch occurred even after */
2021: /* CmdDone was set. */
2022:
2023:
2024: /* Branch Always to assume we will bit bucket some data: */
2025:
2026: descriptorPtr->operation = SWAP( NOP_CMD | kBranchAlways );
2027: descriptorPtr->address = 0;
2028: descriptorPtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclOverrun );
2029: descriptorPtr->result = 0;
2030: descriptorPtr++;
2031:
2032: /* Fix up the DataOverrun code just in case: */
2033:
2034: dp = CCLDescriptor( kcclOverrunMESH );
2035: if ( scsiReq->read )
2036: { dp->cmdDep = SWAP( kMeshDataInCmd | kMeshSeqDMA );
2037: dp = CCLDescriptor( kcclOverrunDBDMA );
2038: dp->operation = SWAP( INPUT_LAST | kBranchIfFalse | 8 );
2039: }
2040: else
2041: { dp->cmdDep = SWAP( kMeshDataOutCmd | kMeshSeqDMA );
2042: dp = CCLDescriptor( kcclOverrunDBDMA );
2043: dp->operation = SWAP( OUTPUT_LAST | kBranchIfFalse | 8 );
2044: }
2045: }/* end IF last of Synchronous transfer */
2046: else
2047: {
2048: /* Async or incomplete Sync. Append Branches to finish this process: */
2049:
2050: /* If this is a partial transfer, set 'incomplete' flag. */
2051:
2052: if ( transferLength > 0 )
2053: gFlagIncompleteDBDMA = TRUE; /* set incomplete */
2054: else gFlagIncompleteDBDMA = FALSE; /* assume complete xfer */
2055:
2056:
2057: if ( gFlagIncompleteDBDMA )
2058: { /* Delete the ccl to clear cmdDone: */
2059: --descriptorPtr; /* see HACK note above. */
2060: }
2061: else if ( totalXferLen > 0 )
2062: { /* If something moved AND (Radar 2298440) xfer completed, */
2063: /* Wait & Branch if problem: */
2064: /* Radar 2272931 - If entire output fits in FIFO, then */
2065: /* the OUTPUT_LAST completes OK without a PhaseMismatch if */
2066: /* the target disconnects right after the command phase. */
2067: bcopy( CCLDescriptor( kcclBrProblem ), descriptorPtr, sizeof( DBDMADescriptor ) );
2068: descriptorPtr++;
2069: }
2070: /* Assume all's well - Branch to get status: */
2071: descriptorPtr->operation = SWAP( NOP_CMD | kBranchAlways );
2072: descriptorPtr->address = 0;
2073: descriptorPtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclGetStatus );
2074: descriptorPtr->result = 0;
2075:
2076: /* If this is a partial transfer, set 'incomplete' flag and */
2077: /* change the Branch from GetStatus to Good: */
2078:
2079: if ( gFlagIncompleteDBDMA )
2080: { /* change last Branch from Status to Good: */
2081: descriptorPtr->cmdDep = SWAP( (UInt32)cclPhysAddr + kcclMESHintr );
2082: ELG( descriptorPtr, transferLength, 'Part', "UpdateCP - built partial CCL." );
2083: }
2084: descriptorPtr++;
2085: }/* end if/ELSE Async or partial xfer */
2086:
2087: ASSERT( descriptorPtr < kDBDMADescriptorEnd );
2088: return;
2089: }/* end UpdateCP */
2090:
2091:
2092: /* StartBucket - Start the channel commands to run the bit bucket. */
2093:
2094: - (void) StartBucket
2095: {
2096: CommandBuffer *cmdBuf;
2097: IOSCSIRequest *scsiReq;
2098: DBDMADescriptor *dp; /* current data descriptor */
2099: UInt32 dbdmaOp; /* Opcode for DBDMA */
2100: UInt32 meshSeq; /* Opcode for MESH request */
2101:
2102:
2103: cmdBuf = gActiveCommand;
2104: scsiReq = cmdBuf->scsiReq;
2105: ELG( cmdBuf, scsiReq, 'Bkt-', "StartBucket" );
2106:
2107: /* Generate MESH "sequence" & DBDMA "operation" for Input or Output: */
2108:
2109: if ( scsiReq->read )
2110: { dbdmaOp = INPUT_MORE | kBranchIfFalse | 8;
2111: meshSeq = kMeshDataInCmd | kMeshSeqDMA;
2112: }
2113: else
2114: { dbdmaOp = OUTPUT_MORE | kBranchIfFalse | 8;
2115: meshSeq = kMeshDataOutCmd | kMeshSeqDMA;
2116: }
2117:
2118: dp = CCLDescriptor( kcclOverrunMESH ); dp->cmdDep = meshSeq;
2119: dp = CCLDescriptor( kcclOverrunDBDMA ); dp->operation = dbdmaOp;
2120:
2121: [ self RunDBDMA : kcclDataXfer stageLabel : kcclStageBucket ];
2122: return;
2123: }/* end StartBucket */
2124:
2125:
2126: /* Set up the channel commands for MsgO phase. */
2127:
2128: - (void) SetupMsgO
2129: {
2130: UInt8 msgoSize;
2131:
2132:
2133: msgOutPtr--; /* treat the last or only byte special (drop ATN) */
2134: msgoSize = msgOutPtr - CCLAddress( kcclMSGOdata );
2135: if( msgoSize == 0 )
2136: { /* Identify byte only: */
2137: CCLWord( kcclMsgoBranch ) = SWAP( NOP_CMD | kBranchAlways );
2138: }
2139: else /* multibyte message - set counts for all but last byte: */
2140: { CCLByte( kcclMsgoMTC ) = msgoSize;
2141: CCLByte( kcclMsgoDTC ) = msgoSize;
2142: /* NOP the BRANCH: */
2143: CCLWord( kcclMsgoBranch ) = SWAP( NOP_CMD );
2144: }
2145: CCLByte( kcclMSGOLast )= *msgOutPtr; /* position last byte */
2146: return;
2147: }/* end SetupMsgO */
2148:
2149:
2150: /* Initialize the autosense area and build the autosense channel command. */
2151:
2152: - (void) InitAutosenseCCL
2153: {
2154: ELG( 0, 0, 'Auto', "InitAutosenseCCL" );
2155: /* Make sure we've allocated enough space in the CCL area. */
2156:
2157: ASSERT( kcclSenseBuffer + kMaxAutosenseByteCount < kcclSenseResult );
2158: bzero( CCLAddress( kcclSenseBuffer ), kMaxAutosenseByteCount );
2159:
2160: /* Copy the Sense CDB to the CDB area and */
2161: /* copy the Sense CCL to the Xfer area */
2162:
2163: /* Copy the Sense CDB & Sense CCL */
2164: bcopy( CCLAddress( kcclSenseCDB ), CCLAddress( kcclCMDOdata ), 6 );
2165: bcopy( CCLAddress( kcclSense ), CCLAddress( kcclDataXfer ), 5 * sizeof( DBDMADescriptor ) );
2166:
2167: /* Set the MESH and DBDMA transfer counts for the command. */
2168: CCLByte( kcclCmdoMTC ) = 6;
2169: CCLByte( kcclCmdoDTC ) = 6;
2170:
2171: /* Set the data transfer count (use a hard-wired value). */
2172:
2173: CCLWord( kcclBatchSize ) = kMaxAutosenseByteCount;
2174:
2175: [ self SetupMsgO ];
2176:
2177: return;
2178: }/* end InitAutosenseCCL */
2179:
2180:
2181: - (void) ClearCPResults
2182: {
2183: register DBDMADescriptor *dp = CCLDescriptor( kcclStart );
2184: register int i;
2185:
2186:
2187: /* Don't clear the reserved areas or prototypes */
2188:
2189: for ( i = (gDescriptorListSize - kcclStart) / sizeof ( DBDMADescriptor ); i; --i )
2190: {
2191: dp->result = 0;
2192: dp++;
2193: }
2194:
2195: return;
2196: }/* end ClearCPResults */
2197:
2198:
2199: @end /* AppleMesh_SCSI(HardwarePrivate) */
2200:
2201:
2202:
2203:
2204: @implementation AppleMesh_SCSI( MeshInterrupt )
2205:
2206:
2207: /* DoHardwareInterrupt - Handle an Interrupt Service message */
2208:
2209: - (void) DoHardwareInterrupt /* called from interruptOccurred */
2210: {
2211: [ self GetHBARegsAndClear : TRUE ]; /* get the MESH registers */
2212: [ self SetIntMask : 0 ]; /* Disable MESH interrupts */
2213:
2214: gFlagReselecting = FALSE;
2215:
2216: if ( g.shadow.mesh.interrupt == 0 )
2217: { /* Interrupts can occur with no bits set in the */
2218: /* interrupt register one way: */
2219: /* - Eating interrupts in the driver (the ASIC */
2220: /* latches the interrupt even though the */
2221: /* driver or Channel Program clears the MESH */
2222: /* interrupt register). */
2223: PAUSE( dbdmaAddr->d_cmdptrlo,
2224: (g.shadow.mesh.busStatus0 << 8) | g.shadow.mesh.busStatus1,
2225: 'ISR?',
2226: "DoHardwareInterrupt - spurious interrupt" );
2227:
2228: if ( !gActiveCommand )
2229: {
2230: [ self selectNextRequest ];
2231: if ( !gActiveCommand && queue_empty( &abortCmdQ ) )
2232: /* if neither new request nor aborting: */
2233: [ self SetIntMask : kMeshIntrMask ];/* Enable interrupts */
2234: }
2235: return;
2236: }/* end IF no bit set in interrupt register */
2237:
2238: dbdma_flush( DBDMA_MESH_SCSI ); /* DBDMA may be hung in */
2239: dbdma_stop( DBDMA_MESH_SCSI ); /* middle of transfer. */
2240: // invalidate_cache_v( (vm_offset_t)cclLogAddr, cclLogAddrSize );
2241:
2242: /* If the DBDMA was running a channel command, handle this */
2243: /* (this could be done at a lower priority level). */
2244:
2245: if ( CCLWord( kcclStageLabel ) )
2246: {
2247: [ self ProcessInterrupt ];
2248: return;
2249: }
2250:
2251: /* This was not a DBDMA completion. */
2252: /* See if the last MESH operation completed */
2253: /* without errors or exceptions. */
2254:
2255: if ( g.shadow.mesh.interrupt == kMeshIntrCmdDone )
2256: {
2257: /* This was presumably a Programmed IO completion. */
2258:
2259: if ( gActiveCommand )
2260: { /* The command has not completed yet. */
2261: /* We need to wait for a phase stabilizing interrupt. */
2262:
2263: PAUSE( 0, 0, 'dhi-', "DoHardwareInterrupt - MESH interrupt problem: need phase stabilizing wait.\n" );
2264: return;
2265: }
2266: else
2267: { /* There is no active command. */
2268: /* This is presumably a bus-free completion. */
2269:
2270: [ self selectNextRequest ]; /* Try to start another request.*/
2271:
2272: if ( !gActiveCommand && queue_empty( &abortCmdQ ) )
2273: /* If still nothing to do: */
2274: [ self SetIntMask : kMeshIntrMask ];/* Re-enable ints */
2275: return;
2276: }
2277: }/* end IF CmdDone without Err or Exc */
2278:
2279: /* None of the above "completion" states occurred. */
2280: /* Either a command completed unsuccessfully, or we */
2281: /* were reselected. First, check for phase mismatch. */
2282: if ( g.shadow.mesh.interrupt == (kMeshIntrCmdDone | kMeshIntrException)
2283: && g.shadow.mesh.exception == kMeshExcPhaseMM )
2284: {
2285: PAUSE( 0, 0, 'DHI-', "DoHardwareInterrupt - MESH interrupt problem: phase mismatch interrupt.\n" );
2286: }
2287: else
2288: { /* Handle reselection and all other problems separately. */
2289: /* (This can be done at a lower priority.) */
2290: [ self ProcessInterrupt ];
2291: }
2292: return;
2293: }/* end DoHardwareInterrupt */
2294:
2295:
2296: /* Respond to a DBDMA channel command completion interrupt */
2297: /* or some error or exception condition. */
2298:
2299: - (void) ProcessInterrupt
2300: {
2301: register CommandBuffer *cmdBuf;
2302: register IOSCSIRequest *scsiReq;
2303: UInt32 stage; /* Stage in the Channel Program */
2304: UInt32 cclIndex; /* Index of CCL descriptor */
2305: UInt32 count; /* transfer count */
2306: UInt8 phase; /* Current bus phase */
2307: IOReturn rc;
2308:
2309:
2310: if ( gActiveCommand == NULL )
2311: {
2312: if ( g.shadow.mesh.exception & kMeshExcResel )
2313: {
2314: [ self HandleReselectionInterrupt ];
2315: }
2316: else
2317: { /* There is no active request and we are not reselecting. */
2318: /* Can get here if Reject/Abort occurs or after a BusFree */
2319: /* command is put in the Sequence register and we exit the */
2320: /* interrupt. */
2321: if ( !queue_empty( &abortCmdQ ) )
2322: {
2323: [ self SetSeqReg : kMeshFlushFIFO ]; /* flush the FIFO */
2324: queue_remove_first( &abortCmdQ, cmdBuf, CommandBuffer*, link );
2325: [ self ioComplete : cmdBuf ];
2326: }/* end IF Aborting disconnected commands */
2327: else
2328: { /* This should be a Bus Free interrupt: */
2329: ELG( 0, 0, 'Int0', "Process interrupt with no active request\n" );
2330: }
2331:
2332: [ self selectNextRequest ];
2333: if ( !gActiveCommand && queue_empty( &abortCmdQ ) )
2334: /* Re-enable ints for reselect */
2335: [ self SetIntMask : (kMeshIntrException | kMeshIntrError) ];
2336: }
2337: return;
2338: }/* end IF had no active command */
2339:
2340: /* There is an active request: */
2341: /* get the stage of the CCL and Switch on it. */
2342: stage = CCLWord( kcclStageLabel );
2343: cmdBuf = gActiveCommand;
2344: scsiReq = cmdBuf->scsiReq;
2345: cclIndex = SWAP( dbdmaAddr->d_cmdptrlo )
2346: - (UInt32) cclPhysAddr;
2347: CCLWord( kcclStageLabel ) = 0;
2348: ASSERT( scsiReq->target == gCurrentTarget && scsiReq->lun == gCurrentLUN );
2349:
2350: /* Analyse where the DBDMA ended up: */
2351:
2352: switch ( stage )
2353: {
2354: case kcclStageGood: /* Normal completion */
2355: [ self DoInterruptStageGood ];
2356: break;
2357:
2358: case kcclStageInit: /* Value before DBDMA runs */
2359: case kcclStageArb: /* Arbitration anomaly */
2360: [ self DoInterruptStageArb ];
2361: break;
2362:
2363: case kcclStageSelA: /* Selection anomaly */
2364: [ self DoInterruptStageSelA ];
2365: break;
2366:
2367: case kcclStageMsgO: /* Message Out */
2368: [ self DoInterruptStageMsgO ];
2369: break;
2370:
2371: case kcclStageCmdO: /* Command stage anomaly */
2372: [ self DoInterruptStageCmdO ];
2373: break;
2374:
2375: case kcclStageXfer:
2376: if ( cmdBuf->flagIsAutosense )
2377: [ self DoInterruptStageXferAutosense ];
2378: else [ self DoInterruptStageXfer ]; /* DMA transfer complete */
2379: break;
2380:
2381: case kcclStageStat: /* Synchronous, odd transfer, data-out */
2382: /* OR no data, disconnect */
2383:
2384: /* Don't use UpdateCurrentIndex here because */
2385: /* kcclStageStat destroys TC with a 1. */
2386: count = CCLWord( kcclBatchSize ); /* Our transfer count */
2387: cmdBuf->currentDataIndex += count; /* Increment data index */
2388: if ( cmdBuf->mem )
2389: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ];
2390:
2391: ELG( count, cmdBuf->currentDataIndex, 'Uidx', "ProcessInterrupt" );
2392: CCLWord( kcclBatchSize ) = 0; /* Clear our count */
2393:
2394: /* Analyze the current bus signals: */
2395:
2396: if ( !(g.shadow.mesh.busStatus0 & kMeshReq) )
2397: { /* Get here if Sync Read or Write is too short as */
2398: /* in reading 512 bytes from a 2K block of CD-ROM. */
2399: [ self StartBucket ];
2400: return;
2401: }/* end IF no REQ signal */
2402:
2403: phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask;
2404: switch ( phase )
2405: {
2406: case kBusPhaseMSGI:
2407: rc = [ self DoMessageInPhase ];
2408: // if ( rc == IO_R_SUCCESS && gActiveCommand )
2409: // break; /* msg processed ok & not disconnect */
2410: /* Enable Exc (for Reselect) and Err interrupts (not CmdDone)*/
2411: break;
2412:
2413: case kBusPhaseDATO:
2414: case kBusPhaseDATI:
2415: /* Get here if Async Read or Write is too short as */
2416: /* in reading 512 bytes from a 2K block of CD-ROM */
2417: [ self StartBucket ];
2418: break;
2419:
2420: default:
2421: PAUSE( scsiReq->target, phase, 'pmm-',
2422: "ProcessInterrupt - expected Status phase.\n" );
2423: break;
2424: }/* end SWITCH on phase */
2425: break;
2426:
2427: case kcclStageBucket:
2428: count = CCLWord( kcclBatchSize ); /* Our transfer count */
2429: cmdBuf->currentDataIndex += count; /* Increment data index */
2430: if ( cmdBuf->mem )
2431: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ];
2432: CCLWord( kcclBatchSize ) = 0; /* Clear our count */
2433:
2434: ELG( count, cmdBuf->currentDataIndex, 'Buck', "ProcessInterrupt - bit bucket done.\n" );
2435:
2436: // scsiReq->driverStatus = SR_IOST_DMAOR; /* set DMA OverRun error */
2437:
2438: [ self SetSeqReg : kMeshFlushFIFO ]; /* flush the FIFO */
2439: [ self RunDBDMA : kcclGetStatus stageLabel : kcclStageStat ];
2440: break;
2441:
2442: case kcclStageMsgI: /* Message-in: */
2443: case kcclStageFree: /* Bus free: */
2444: default: /* Can't happen? */
2445: PAUSE( cclIndex, stage, 'P i-', "ProcessInterrupt - strange or unknown interrupt for device.\n" );
2446: break;
2447: }/* end SWITCH on Channel Program stage */
2448: return;
2449: }/* end ProcessInterrupt */
2450:
2451:
2452: /* The channel command (and, hence, the IO request) ran to */
2453: /* completion without problems. Complete this IO request */
2454: /* and try to start another. */
2455:
2456: - (void) DoInterruptStageGood
2457: {
2458: register CommandBuffer *cmdBuf;
2459: register IOSCSIRequest *scsiReq;
2460: UInt32 totalXferLen;
2461: IOMemoryDescriptorState state;
2462: UInt8 byte;
2463:
2464:
2465: ASSERT( gActiveCmd && gActiveCmd->scsiReq );
2466:
2467: // [ self SetSeqReg : kMeshEnableReselect ]; // done by CCL
2468:
2469: cmdBuf = gActiveCommand;
2470: scsiReq = cmdBuf->scsiReq;
2471:
2472: /* Retrieve the total number of bytes transferred */
2473: /* in the last data phase. */
2474:
2475: cmdBuf->flagRequestSelectOK = FALSE;
2476: totalXferLen = CCLWord( kcclBatchSize );
2477: CCLWord( kcclBatchSize ) = 0;
2478:
2479: ELG( scsiReq, totalXferLen, 'Good', "DoInterruptStageGood" );
2480:
2481: if ( cmdBuf->flagIsAutosense )
2482: {
2483: /* We are completing an autosense command. */
2484: /* Copy the status byte (which had better be */
2485: /* "good" ) from the CCL to the autosense status*/
2486: /* and complete the IO request. The autosense */
2487: /* data itself was copied into the user buffer */
2488: /* by a previous 'Xfer' interrupt. */
2489:
2490: cmdBuf->autosenseStatus = CCLByte( kcclStatusData );
2491: [ self deactivateCmd : cmdBuf ];
2492: [ self ioComplete : cmdBuf ];
2493: }
2494: else
2495: { /* We are completing a normal command. */
2496: /* Update the transfer count and current data pointer. */
2497:
2498: cmdBuf->currentDataIndex += totalXferLen;
2499: if ( cmdBuf->mem )
2500: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ];
2501:
2502: if ( gFlagIncompleteDBDMA == FALSE )
2503: {
2504: /* Yes, the IO is really complete: */
2505:
2506: scsiReq->scsiStatus = CCLByte( kcclStatusData );
2507: scsiReq->driverStatus = SR_IOST_GOOD;
2508:
2509: /* If this was an Inquiry, peek at the data */
2510: /* for Synchronous and Queuing support: */
2511:
2512: if ( (*(UInt8*)&scsiReq->cdb == kScsiCmdInquiry)
2513: && (cmdBuf->currentDataIndex > 7) )
2514: {
2515: [ cmdBuf->mem state : &state ]; /* save context */
2516: [ cmdBuf->mem setPosition : 7 ];
2517: if ( [ cmdBuf->mem readFromClient : &byte count : 1 ] == 1 )
2518: {
2519: gPerTargetData[ scsiReq->target ].inquiry_7 = byte;
2520: ELG( scsiReq->target, byte, 'Inq+',
2521: "DoInterruptStageGood - peek at Inquiry data" );
2522: }
2523: [ cmdBuf->mem setState : &state ]; /* restore context */
2524: }
2525: [ self deactivateCmd : cmdBuf ];
2526: [ self ioComplete : cmdBuf ];
2527: }
2528: else
2529: { /* The CCL ended, but the caller expected more data. */
2530: /* Restart the CCL. */
2531: /* Don't regenerate arbitration or command stuff. */
2532:
2533: [ self UpdateCP : TRUE ];
2534: [ self RunDBDMA : kcclDataXfer stageLabel : kcclStageXfer ];
2535: return;
2536: }/* end ELSE need to continue Channel Program */
2537: }/* end ELSE not AutoSense */
2538:
2539: /* Since IO completed (otherwise, we would have exited in the */
2540: /* "return" above), check whether a reselection attempt */
2541: /* is piggy-backed on top of the good DBDMA completion. */
2542:
2543: if ( g.shadow.mesh.exception & kMeshExcResel )
2544: [ self HandleReselectionInterrupt ];
2545: else /* Nothing happening. Try to start another request. */
2546: {
2547: [ self selectNextRequest ];
2548: if ( !gActiveCommand && queue_empty( &abortCmdQ ) )
2549: /* Re-enable ints for reselect */
2550: [ self SetIntMask : (kMeshIntrException | kMeshIntrError) ];
2551: }
2552:
2553: return;
2554: }/* end DoInterruptStageGood */
2555:
2556:
2557: /* Process the autosense data transfer phase. IO is not complete. */
2558: /* There are several reasons why we might get here: */
2559: /* -- autosense completion (which could be a separate stage) */
2560: /* -- DMA completion with more DMA to do */
2561: /* -- Bus phase mismatch (short transfer or disconnect) */
2562:
2563: - (void) DoInterruptStageXferAutosense
2564: {
2565: register CommandBuffer *cmdBuf = gActiveCommand;
2566: IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
2567: UInt32 residual;
2568: UInt32 count;
2569:
2570:
2571: cmdBuf->flagRequestSelectOK = FALSE;
2572:
2573: /* An autosense Data In transfer is complete. Copy the */
2574: /* autosense data from our private buffer to the */
2575: /* caller's sense_data area and restart IO to get the */
2576: /* status and command-complete message byte. */
2577:
2578: residual = SWAP( CCLWord( kcclSenseResult ) ) & 0xFF;
2579: count = kMaxAutosenseByteCount - residual;
2580: ASSERT( count <= sizeof( esense_reply_t ) );
2581: bcopy( CCLAddress( kcclSenseBuffer ), &scsiReq->senseData, count );
2582:
2583: /* Driver Kit does not return "sense valid" or */
2584: /* the actual sense transfer count. */
2585: /* Restart the channel command to fetch the */
2586: /* status byte and Command Completion byte. */
2587:
2588: [ self RunDBDMA : kcclGetStatus stageLabel : kcclStageStat ];
2589: return;
2590: }/* end DoInterruptStageXferAutosense */
2591:
2592:
2593: /* Process a normal data phase interrupt. IO is not complete. */
2594: /* There are several reasons why we might get here: */
2595: /* -- autosense completion (which could be a separate stage) */
2596: /* -- DMA completion with more DMA to do */
2597: /* -- Bus phase mismatch (short transfer or disconnect, MsgIn) */
2598: /* Note that we know that we are not in autosense. */
2599:
2600: - (void) DoInterruptStageXfer
2601: {
2602: register CommandBuffer *cmdBuf = gActiveCommand;
2603: UInt32 count; /* DMA transfer count */
2604: UInt8 phase; /* Current bus phase */
2605: IOReturn rc;
2606: int goAround;
2607:
2608:
2609: cmdBuf->flagRequestSelectOK = FALSE;
2610: count = cmdBuf->currentDataIndex;
2611:
2612: [ self UpdateCurrentIndex ];
2613:
2614: do
2615: { goAround = FALSE; /* assume loop not repeated */
2616: [ self SetSeqReg : kMeshFlushFIFO ];
2617:
2618: /* We've cleaned up the mess from the previous data transfer. */
2619: /* Look at the current bus phase. The channel command waited */
2620: /* for REQ to be set before interrupting the processor. */
2621:
2622: phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask;
2623: ASSERT( g.shadow.mesh.busStatus0 & kMeshReq ); /* REQ is set, right? */
2624:
2625: switch ( phase )
2626: {
2627: case kBusPhaseSTS:
2628: gFlagIncompleteDBDMA = FALSE; /* indicate no-more-data */
2629: [ self RunDBDMA : kcclGetStatus stageLabel : kcclStageStat ];
2630: break;
2631:
2632: case kBusPhaseMSGI:
2633: rc = [ self DoMessageInPhase ];
2634: if ( rc == IO_R_SUCCESS && gActiveCommand )
2635: goAround = TRUE; /* msg ok & not disconnect */
2636: break;
2637:
2638: case kBusPhaseDATO:
2639: case kBusPhaseDATI:
2640: if ( count != cmdBuf->currentDataIndex )
2641: { /* Data phase had already started: */
2642: PAUSE( 0, phase, 'dat-', "DoInterruptStageXfer - unexpected Data phase.\n" );
2643: }
2644: else
2645: { /* try starting data phase again */
2646: [ self RunDBDMA : kcclDataXfer stageLabel : kcclStageXfer ];
2647: }
2648: break;
2649:
2650: default:
2651: PAUSE( cmdBuf->scsiReq->target, phase, 'Phs-', "DoInterruptStageXfer - bogus phase.\n" );
2652: break;
2653: }/* end SWITCH on phase */
2654: } while ( goAround );
2655: return;
2656: }/* end DoInterruptStageXfer */
2657:
2658:
2659: /* DoInterruptStageArb - Process an anomaly during arbitration. */
2660:
2661: - (void) DoInterruptStageArb
2662: {
2663: ASSERT( gActiveCommand );
2664: ELG( 0, 0, 'Arb-', "DoInterruptStageArb - Lost arbitration.\n" );
2665:
2666: [ self pushbackCurrentRequest : gActiveCommand ];
2667: ASSERT( gActiveCommand == NULL );
2668:
2669: if ( g.shadow.mesh.exception & kMeshExcResel )
2670: { if ( g.shadow.mesh.error & kMeshErrDisconnected )
2671: {
2672: /* 18sep98 - Sometimes MESH gets real confused when its */
2673: /* arbitration loses to a target's reselect arbitration. */
2674: /* The registers show Exc:ArbLost, Resel and Err:UnExpDisc. */
2675: /* The FIFO count is 1 (should be SCSI ID bits) while the */
2676: /* BusStatus0,1 registers show IO and Sel both of which are */
2677: /* set by the reselecting Target. */
2678: /* The SCSI bus anaylzer shows the following events occcurring */
2679: /* within a few microseconds of BSY being set by the target: */
2680: /* bus free for at least hundreds of microseconds */
2681: /* Target raises BSY along with its ID bit */
2682: /* Target raises SEL */
2683: /* Target raises IO to indicate reselection */
2684: /* Target adds MESH's ID bit */
2685: /* Target drops BSY */
2686: /* MESH raises BSY to accept reselection */
2687: /* **** MESH drops BSY **** here is where MESH is confused */
2688: /* Target stays on bus for 250 milliseconds. */
2689: /* To solve this problem, whack MESH with a RstMESH. */
2690:
2691: ELG( ' Rst', 'MESH', 'UEP-', "DoInterruptStageArb - Resel/Unexpected Disconnect.\n" );
2692: [ self SetSeqReg : kMeshResetMESH ]; /* completes quickly */
2693: [ self GetHBARegsAndClear : TRUE ]; /* clear cmdDone */
2694: [ self SetSeqReg : kMeshEnableReselect ];
2695: [ self SetIntMask : kMeshIntrMask ]; /* Enable Interrupts */
2696: return; /* now wait for another reselect interrupt */
2697: }
2698: [ self HandleReselectionInterrupt ];
2699: }
2700: else
2701: { /* 22sep97 - lost arbitration without reselection. */
2702: /* Probably lost the reselect condition processing an */
2703: /* error or something. */
2704: ELG( 0, 0, 'ARB-', "DoInterruptStageArb - Lost arbitration without reselect.\n" );
2705: }
2706: return;
2707: }/* end DoInterruptStageArb */
2708:
2709:
2710: /* Process an anomaly during target selection. */
2711:
2712: - (void) DoInterruptStageSelA
2713: {
2714: ASSERT( gActiveCommand );
2715: [ self SetSeqReg : kMeshEnableReselect ];
2716: [ self SetSeqReg : kMeshBusFreeCmd ]; /* clear ATN signal MESH left on */
2717: [ self killCurrentRequest ];
2718: [ self GetHBARegsAndClear : FALSE ]; /* check MESH registers */
2719: [ self SetIntMask : kMeshIntrMask ]; /* Enable Interrupts */
2720: return;
2721: }/* end DoInterruptStageSelA */
2722:
2723:
2724: /* Process an anomaly during Message-Out phase. */
2725: /* Target probably doing Message Reject (0x07). */
2726:
2727: - (void) DoInterruptStageMsgO
2728: {
2729: UInt8 phase;
2730: IOReturn rc;
2731:
2732:
2733: ASSERT( gActiveCommand );
2734:
2735: phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask; /* phase me */
2736: PAUSE( gActiveCommand, phase, 'Mgo-',
2737: "DoInterruptStageMsgO - error during msg-out phase.\n" );
2738:
2739: switch ( phase )
2740: {
2741: case kBusPhaseMSGI:
2742: rc = [ self DoMessageInPhase ];
2743: if ( rc != IO_R_SUCCESS )
2744: {
2745: PAUSE( 0, rc, ' MI-',
2746: "DoInterruptStageMsgO - MsgIn during MsgOut phase.\n" );
2747: // ??? need to get to bus-free from here
2748: // ??? need to blow off the IO
2749: }
2750: else
2751: { ELG( 0, gMsgInFlag, 'rej?', "DoInterruptStageMsgO - got MsgIn.\n" );
2752: if ( gMsgInFlag & kFlagMsgIn_Reject )
2753: [ self AbortActiveCommand ];
2754: }
2755: break;
2756:
2757: default:
2758: PAUSE( gMsgInFlag, phase, 'mgo-',
2759: "DoInterruptStageMsgO - unknown phase during MsgOut phase.\n" );
2760: break;
2761: }
2762: return;
2763: }/* end DoInterruptStageMsgO */
2764:
2765:
2766: /* DoInterruptStageCmdO - Process an anomaly during command stage. */
2767:
2768: - (void) DoInterruptStageCmdO
2769: {
2770: register CommandBuffer *cmdBuf;
2771: UInt8 phase;
2772: IOReturn rc;
2773:
2774:
2775: /* See if this is part of the normal AbortTag/BusDeviceReset process: */
2776:
2777:
2778: if ( !queue_empty( &abortCmdQ ) )
2779: {
2780: [ self SetSeqReg : kMeshFlushFIFO ]; /* flush the FIFO */
2781: cmdBuf = (CommandBuffer*)queue_first( &abortCmdQ );
2782: ELG( cmdBuf, 0, 'Abo-', "DoInterruptStageCmdO - Aborting." );
2783: queue_remove( &abortCmdQ, cmdBuf, CommandBuffer*, link );
2784: [ self ioComplete : cmdBuf ];
2785: [ self AbortDisconnectedCommand ]; /* do the next, if any */
2786: return;
2787: }
2788:
2789: /* Not aborting - something bad happened: */
2790:
2791: ASSERT( gActiveCommand );
2792: cmdBuf = gActiveCommand;
2793: cmdBuf->flagRequestSelectOK = FALSE;
2794: phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask; /* phase me */
2795: ELG( cmdBuf, phase, 'CMD?', "DoInterruptStageCmdO - anomaly during Cmd phase.\n" );
2796:
2797: if ( phase == kBusPhaseMSGI )
2798: { /* We are probably negotiating SDTR or */
2799: /* getting rejected on a nonzero LUN. */
2800: rc = [ self DoMessageInPhase ];
2801: if ( rc != IO_R_SUCCESS )
2802: {
2803: PAUSE( 0, rc, ' mi-',
2804: "DoInterruptStageCmdO - MsgIn during Cmd phase.\n" );
2805: }
2806: else
2807: { /* Message processed - where do we go from here? */
2808:
2809: if ( !gActiveCommand ) /* if Rejected, */
2810: return; /* return */
2811:
2812: phase = g.shadow.mesh.busStatus0 & kMeshPhaseMask;
2813: switch ( phase )
2814: {
2815: case kBusPhaseSTS:
2816: [ self RunDBDMA : kcclCmdoStage stageLabel : kcclStageInit ];
2817: break;
2818:
2819: case kBusPhaseMSGO:
2820: msgOutPtr = (UInt8*)CCLAddress( kcclMSGOdata );
2821: [ self SetupMsgO ];
2822: [ self RunDBDMA : kcclMsgoStage stageLabel : kcclStageInit ];
2823: break;
2824:
2825: case kBusPhaseCMD:
2826: [ self RunDBDMA : kcclCmdoStage stageLabel : kcclStageInit ];
2827: break;
2828: }
2829: }
2830: }
2831: else if ( phase == kBusPhaseSTS ) /* Probably Check Condition */
2832: { /* Perhaps block # invalid */
2833: gFlagIncompleteDBDMA = FALSE; /* indicate no-more-data */
2834: [ self RunDBDMA : kcclGetStatus stageLabel : kcclStageStat ];
2835: }
2836: else
2837: {
2838: PAUSE( 0, phase, 'Phs?', "DoInterruptStageCmdO - error during Command phase.\n" );
2839: }
2840: return;
2841: }/* end DoInterruptStageCmdO */
2842:
2843:
2844: /* We are in MSGI phase. Read the bytes. Return TRUE if an entire */
2845: /* message was read (we may still be in MSGI phase). Note that this */
2846: /* is done by programmed IO, which will fail (logging the error) if */
2847: /* the target sets MSGI but does not send us a message quickly enough. */
2848: /* This method is called from the normal data transfer interrupt when */
2849: /* the target enters message in phase, and from the reselection */
2850: /* interrupt handler when we read a valid reselection target ID. */
2851: /* Note that MESH interrupts are disabled on exit. */
2852:
2853: - (IOReturn) DoMessageInPhase
2854: {
2855: register UInt8 messageByte;
2856: UInt32 index = 0;
2857: IOReturn ioReturn = IO_R_SUCCESS;
2858:
2859:
2860: /* We do not necessarily have a valid command in this method. */
2861: /* While we're processing Message-In bytes, we don't want any */
2862: /* MESH hardware interrupts. */
2863:
2864: [ self SetIntMask : 0 ]; /* no MESH interrupt latching */
2865: [ self SetSeqReg : kMeshFlushFIFO ]; /* Flush the FIFO */
2866:
2867: gMsgInCount = 0;
2868: gMsgInState = kMsgInInit;
2869:
2870: while ( gMsgInState != kMsgInReady /* Disconnect makes gActiveCommand */
2871: && ioReturn == IO_R_SUCCESS ) /* go away */
2872: {
2873: meshAddr->transferCount1 = 0;
2874: meshAddr->transferCount0 = 1; /* get single byte */
2875: [ self SetSeqReg : kMeshMessageInCmd ]; /* issue MsgIn */
2876:
2877: ioReturn = [ self WaitForMesh : TRUE ]; /* wait for cmdDone */
2878: if ( ioReturn != IO_R_SUCCESS )
2879: {
2880: PAUSE( gCurrentTarget, ioReturn, 'Mgi-', "DoMessageInPhase - Target hung: message in timeout.\n" );
2881: break; /* Bus reset here? */
2882: }
2883:
2884: if ( (g.shadow.mesh.exception & kMeshExcPhaseMM)
2885: || (g.shadow.mesh.busStatus0 & kMeshPhaseMask) != kBusPhaseMSGI )
2886: {
2887: break; /* exit loop if no longer in Msg-In phase */
2888: }
2889:
2890: if ( g.shadow.mesh.FIFOCount == 0 )
2891: {
2892: PAUSE( gCurrentTarget, 0, 'mgi-', "DoMessageInPhase - no message byte.\n" );
2893: break;
2894: }
2895:
2896: messageByte = meshAddr->xFIFO;
2897:
2898: ASSERT( index < 256 );
2899: gMsgInBuffer[ index++ ] = messageByte;
2900:
2901: switch ( gMsgInState )
2902: {
2903: case kMsgInInit:
2904: /* This is the first message byte. Check for 1-byte codes. */
2905: if ( messageByte == kScsiMsgCmdComplete
2906: || (messageByte >= kScsiMsgOneByteMin && messageByte <= kScsiMsgOneByteMax)
2907: || messageByte >= kScsiMsgIdentify )
2908: {
2909: gMsgInState = kMsgInReady;
2910: }
2911: else if ( messageByte >= kScsiMsgTwoByteMin
2912: && messageByte <= kScsiMsgTwoByteMax )
2913: {
2914: /* This is a two-byte message. */
2915: /* Set the count and read the next byte. */
2916:
2917: gMsgInState = kMsgInReading; /* Need one more */
2918: gMsgInCount = 1;
2919: }
2920: else
2921: { /* This is an extended message. */
2922: /* The next byte has the count. */
2923: gMsgInState = kMsgInCounting;
2924: }
2925: break;
2926:
2927: case kMsgInCounting: /* Count byte of multi-byte message: */
2928: gMsgInCount = messageByte;
2929: gMsgInState = kMsgInReading;
2930: break;
2931:
2932: case kMsgInReading: /* Body of multi-byte message: */
2933: if ( --gMsgInCount <= 0 )
2934: gMsgInState = kMsgInReady;
2935: break;
2936:
2937: default:
2938: ASSERT( gMsgInState ); /* Bogus state */
2939: PAUSE( 0, 0, 'Msg-', "DoMessageInPhase - Bogus MSGI state!\n" );
2940: gMsgInState = kMsgInReady;
2941: break;
2942: }/* end SWITCH on MSGI state */
2943:
2944: if ( gMsgInState == kMsgInReady )
2945: {
2946: [ self ProcessMSGI ];
2947: gMsgInState = kMsgInInit;
2948: index = 0;
2949: if ( gMsgInBuffer[0] == kScsiMsgDisconnect )
2950: ioReturn = IO_R_IO; /* break out of WHILE loop */
2951:
2952: if ( gMsgInFlag & kFlagMsgIn_Reject )
2953: {
2954: [ self AbortActiveCommand ];
2955: break;
2956: }
2957:
2958: if ( gFlagReselecting )
2959: break; /* Take Identify only - leave +ACK */
2960: }/* end IF have a complete message-in to process */
2961: }/* end WHILE there are more message bytes */
2962:
2963: /***** If the target switches out of MSGI phase without *****/
2964: /***** sending a complete message, we should do some *****/
2965: /***** sort of error recovery. *****/
2966:
2967: if ( gMsgInState != kMsgInInit )
2968: {
2969: PAUSE( gCurrentTarget, gMsgInState, 'MGI-', "DoMessageInPhase - incomplete message.\n" );
2970: if ( ioReturn == IO_R_SUCCESS )
2971: ioReturn = IO_R_IO; /* General IO error */
2972: }
2973:
2974: return ioReturn;
2975: }/* end DoMessageInPhase */
2976:
2977:
2978: /* ProcessMSGI - DoMessageInPhase has read a complete message. */
2979: /* Process it (this will probably change our internal state). */
2980:
2981: - (void) ProcessMSGI
2982: {
2983: /* Note that, during reselection, we may not have */
2984: /* a current target or LUN, nor possibly a valid command */
2985:
2986: register CommandBuffer *cmdBuf;
2987: register IOSCSIRequest *scsiReq;
2988:
2989: UInt8 sdtr;
2990: UInt8 currentTarget, currentLUN;
2991: UInt8 period, offset;
2992: UInt8 targetResponse; /* responding or requesting? */
2993:
2994:
2995: cmdBuf = gActiveCommand; /* May be NULL */
2996: scsiReq = (cmdBuf == NULL) ? NULL : cmdBuf->scsiReq;
2997: if ( scsiReq )
2998: { currentTarget = scsiReq->target;
2999: currentLUN = scsiReq->lun;
3000: ASSERT( currentTarget == gCurrentTarget && currentLUN == gCurrentLUN );
3001: }
3002: else
3003: { currentTarget = gCurrentTarget;
3004: currentLUN = gCurrentLUN;
3005: }
3006:
3007: ELG( 0, *(UInt32*)gMsgInBuffer, '<Msg', "ProcessMSGI" );
3008:
3009: switch ( gMsgInBuffer[0] )
3010: {
3011: case kScsiMsgCmdComplete:
3012: if ( cmdBuf )
3013: {
3014: /* This command is complete. Clear interrupts and */
3015: /* allow subsequent MESH interrupts. Then tell the */
3016: /* MESH to wait for the target to release the bus. */
3017:
3018: [ self SetSeqReg : kMeshEnableReselect ];
3019: [ self SetSeqReg : kMeshBusFreeCmd ]; /* cause Int */
3020:
3021: if ( cmdBuf->flagIsAutosense == FALSE )
3022: {
3023: if ( scsiReq )
3024: scsiReq->scsiStatus = CCLByte( kcclStatusData );
3025:
3026: /* Driver Kit does not return the command-complete byte. */
3027:
3028: }
3029: [ self ioComplete : cmdBuf ];
3030: }
3031: goto exit; /* Don't exit through the SWITCH end */
3032:
3033: case kScsiMsgLinkedCmdComplete:
3034: case kScsiMsgLinkedCmdCompleteFlag:
3035: PAUSE( gCurrentTarget, 0, 'pmi-', "ProcessMSGI - linked command complete not supported.\n" );
3036: [ self AbortActiveCommand ];
3037: break;
3038:
3039: case kScsiMsgNop:
3040: break;
3041:
3042: case kScsiMsgRestorePointers:
3043: if ( cmdBuf )
3044: {
3045: cmdBuf->currentDataIndex = cmdBuf->savedDataIndex;
3046: if ( cmdBuf->mem )
3047: [ cmdBuf->mem setState : &cmdBuf->savedDataState ];
3048: }
3049: break;
3050:
3051: case kScsiMsgSaveDataPointers:
3052: if ( cmdBuf )
3053: {
3054: cmdBuf->savedDataIndex = cmdBuf->currentDataIndex;
3055: if ( cmdBuf->mem )
3056: [ cmdBuf->mem state : &cmdBuf->savedDataState ];
3057: }
3058: break;
3059:
3060: case kScsiMsgDisconnect:
3061: /* Driver Kit does not support automatic Save Data Pointers on */
3062: /* Disconnect. */
3063: /* Move this request to the disconnect queue, enable reselection, */
3064: /* re-enable MESH interrupts, and wait (here) for bus free, but */
3065: /* don't eat the interrupt. */
3066:
3067: gMsgInFlag |= kFlagMsgIn_Disconnect;
3068: [ self disconnect ]; /* requeue active */
3069: [ self SetSeqReg : kMeshEnableReselect ]; /* enable reselect */
3070: [ self SetIntMask : kMeshIntrMask ]; /* Enable Ints */
3071: [ self SetSeqReg : kMeshBusFreeCmd ]; /* issue BusFree */
3072:
3073: /* wait for Bus Free command to complete: */
3074:
3075: [ self WaitForMesh : FALSE ]; /* don't clear possible reselect */
3076:
3077: /* Interrupt for bus-free now latched. Prevent a double interrupt, */
3078: /* 1 from bus-free + 1 from reselect from occurring. */
3079: /* This fixes the following BADNESS: */
3080: /* Issue bus-free for disconnect. */
3081: /* Interrupt occurs in microseconds - even before exiting */
3082: /* "interruptOccurred" routine. */
3083: /* Mach queues message to driverKit. */
3084: /* Exit "interruptOccurred" routine. */
3085: /* DriverKit dequeues and starts handling 1st Mach message. */
3086: /* Interrupt occurs for reselect while driverKit running. */
3087: /* Mach queues 2nd message to driverKit. */
3088: /* DriverKit invokes MESH driver for 1st msg. */
3089: /* MESH driver sees cmdDone fm bus-free AND reselect exception.*/
3090: /* MESH driver handles reselect by setting up and running */
3091: /* DBDMA. MESH driver exits. */
3092: /* DriverKit invokes MESH driver with 2nd Mach message. */
3093: /* MESH driver handles this as a DBDMA completion and royally */
3094: /* screws up. */
3095:
3096: g.intLevel |= kLevelLatched; /* set latched-interrupt flag */
3097: [ self SetIntMask : 0 ]; /* prevent multiple MESH ints */
3098: break;
3099:
3100: case kScsiMsgRejectMsg:
3101: ELG( currentTarget, gMsgOutFlag, 'Rej-', "ProcessMSGI - Reject." );
3102: gMsgInFlag |= kFlagMsgIn_Reject;
3103: break;
3104:
3105: case kScsiMsgSimpleQueueTag:
3106: msgInTagType = gMsgInBuffer[0];
3107: msgInTag = gMsgInBuffer[1];
3108: ELG( 0, msgInTag, '=Tag', "Simple Queue Tag" );
3109: break;
3110:
3111: case kScsiMsgExtended:
3112:
3113: /* Multi-byte message, presumably Synchronous Negotiation: */
3114:
3115: switch ( gMsgInBuffer[ 2 ] ) /* switch on the msg code byte */
3116: {
3117: case kScsiMsgSyncXferReq: /* handle sync negotiation: */
3118: if ( scsiReq == NULL ) // ??? can this happen?
3119: {
3120: PAUSE( currentTarget, 0, 'pMI-', "ProcessMSGI - attempted to negotiate SDTR without a nexus.\n" );
3121: [ self AbortActiveCommand ];
3122: }
3123: else
3124: { /* Get period in nanoseconds */
3125: period = gMsgInBuffer[ 3 ] * 4; /* SCSI uses 4ns granularity */
3126:
3127: /* determine target responding or initiating? */
3128: targetResponse = gPerTargetData[ scsiReq->target ].negotiateSDTR;
3129: gPerTargetData[ scsiReq->target ].negotiateSDTR = 0;
3130: if ( targetResponse )
3131: {
3132: if ( gMsgInBuffer[ 4 ] == 0 ) /* check offset */
3133: {
3134: sdtr = kSyncParmsAsync; /* Offset == 0 implies async */
3135: }
3136: else /* synchronous: */
3137: {
3138: if ( period == 100 ) /* special-case 100=FAST */
3139: {
3140: sdtr = kSyncParmsFast & 0x0F;
3141: }
3142: else /* Older CD-ROMs get here. */
3143: { /* The MESH manual says: */
3144: /* period = 4 * clk + 2 * clk * P */
3145: /* where: */
3146: /* period is the target nanoseconds */
3147: /* clk is the MESH clock rate which is */
3148: /* 20 nanoseconds for a 50 MHz clock */
3149: /* P is the 1-nibble period code we stuff */
3150: /* in the syncParms register */
3151: /* So: */
3152: /* period = 4 * 20 + 2 * 20 * P */
3153: /* period = 80 + 40 * P */
3154: /* P = (period - 80) / 40 */
3155: /* Since P must round up for safety: */
3156: /* P = ((period - 80) + 39) / 40 */
3157: /* P = (period - 41) / 40 */
3158: /* A value of P == 3 results in 5 MB/s */
3159: sdtr = (UInt8)((period - 41) / 40);
3160: }
3161: #ifdef CRAP
3162: /* If period is longer than 200 ns resulting */
3163: /* in less than 5 MB/s, renegotiate async later.*/
3164: if ( period >= 200 )
3165: gPerTargetData[ scsiReq->target ].negotiateSDTR = kSyncParmsAsync;
3166: #endif /* CRAP */
3167: }/* end ELSE have offset ergo Synchronous */
3168:
3169: /* OR in the offset. */
3170: sdtr |= (gMsgInBuffer[ 4 ] << 4);
3171: }/* end IF Target is responding to negotiation */
3172:
3173: else /* target is initiating negotiation: */
3174: {
3175: msgOutPtr = (UInt8*)CCLAddress( kcclMSGOdata );
3176: *msgOutPtr++ = kScsiMsgExtended; /* 0x01 Ext Msg */
3177: *msgOutPtr++ = 0x03; /* 0x03 Message Len */
3178: *msgOutPtr++ = kScsiMsgSyncXferReq; /* 0x01 SDTR code */
3179: offset = gMsgInBuffer[ 4 ];
3180: if ( offset == 0 ) /* Offset == 0 means async: */
3181: {
3182: *msgOutPtr++ = 0; /* clear period byte */
3183: *msgOutPtr++ = 0; /* offset byte = 0 for async*/
3184: sdtr = kSyncParmsAsync; /* set value for MESH reg */
3185: }
3186: else /* have offset ergo sync: */
3187: {
3188: if ( offset > 15 )
3189: offset = 15; /* MESH can only handle 15 */
3190:
3191: if ( period <= 100 ) /* special-case 100=FAST */
3192: period = 100;
3193: else
3194: { /* round up to MESH's 40 ns granularity */
3195: period = ((period + 39) / 40) * 40;
3196: }
3197: *msgOutPtr++ = period / 4; /* SCSI 4ns granularity */
3198: *msgOutPtr++ = offset;
3199: sdtr = (offset << 8) | (UInt8)((period - 41) / 40);
3200: }/* end target is negotiating Sync */
3201: /* respond to target: */
3202: [ self RunDBDMA : kcclMsgoStage stageLabel : kcclStageInit ];
3203: gPerTargetData[ scsiReq->target ].negotiateSDTR = 0;
3204: }/* end ELSE target is initiating negotiation */
3205:
3206: meshAddr->syncParms = sdtr;
3207: SynchronizeIO();
3208: gPerTargetData[ scsiReq->target ].syncParms = sdtr;
3209: ELG( *(UInt32*)&gMsgInBuffer[0], gMsgInBuffer[4]<<24 | sdtr, 'SDTR', "ProcessMSGI - SDTR" );
3210: } /* end ELSE have a nexus */
3211: break;
3212:
3213: default:
3214: PAUSE( currentTarget, gMsgInBuffer[0], 'PMi-', "ProcessMSGI - unsupported extended message.\n" );
3215: [ self AbortActiveCommand ];
3216: break;
3217: }/* end SWITCH on extended message code */
3218: break;
3219:
3220: default:
3221: if ( gMsgInBuffer[0] >= kScsiMsgIdentify )
3222: {
3223: ASSERT( gCurrentTarget != kInvalidTarget );
3224: ASSERT( gCurrentLUN == kInvalidLUN );
3225: gCurrentLUN = gMsgInBuffer[0] & kScsiMsgIdentifyLUNMask;
3226: currentLUN = gCurrentLUN;
3227: }
3228: else
3229: {
3230: PAUSE( currentTarget, gMsgInBuffer[0], 'mi -', "ProcessMSGI - unsupported message: rejected.\n" );
3231: [ self AbortActiveCommand ];
3232: }
3233: }/* end SWITCH on message selection */
3234:
3235: exit:
3236: return;
3237: }/* end ProcessMSGI */
3238:
3239:
3240: /* Process a reselection interrupt. */
3241:
3242: - (void) HandleReselectionInterrupt
3243: {
3244: IOReturn ioReturn;
3245:
3246:
3247: ASSERT( gActiveCommand == NULL );
3248:
3249: gFlagReselecting = TRUE;
3250:
3251: /* Sometimes MESH gives a bogus Disconnected error during Reselection. */
3252: /* 31mar98 - Issuing an Abort message, causes "unexpected disconnect". */
3253: /* When Err:UnexpDisc and Exc:Resel are simultaneously set, the */
3254: /* busStatus0,1 registers may not be current. */
3255: if ( g.shadow.mesh.error & kMeshErrDisconnected )
3256: {
3257: [ self SetSeqReg : kMeshBusFreeCmd ];
3258: [ self WaitForMesh : TRUE ]; // now maybe busStatus0,1 are live
3259: PAUSE( 0, 0, 'Dsc-',
3260: "HandleReselectionInterrupt: Caught disconnected glitch\n" );
3261: }/* End IF bus disconnect error */
3262:
3263: /* Read the target ID (which should be our initiator ID OR'd with the */
3264: /* Target and the Identify byte with the reselecting LUN. Store this */
3265: /* in gTargetID and gTargetLUN. Note that, during reselection, we will */
3266: /* have a NULL gCurrentCommand and a valid gCurrentTarget and gCurrentLUN. */
3267: /* If we get a valid reselection target, call the message in phase */
3268: /* directly to read the LUN byte. */
3269: /* @return TRUE if successful. */
3270:
3271: msgInTag = 0;
3272: if ( meshAddr->FIFOCount == 0 )
3273: {
3274: PAUSE( 0, 0, 'HRI-', "HandleReselectionInterrupt - Empty FIFO in reselection.\n" );
3275: return;
3276: }
3277: else /* get the Target ID bit from the bus out of the FIFO */
3278: { /* then, get the msg-in Identify byte for the LUN. */
3279: if ( [ self getReselectionTargetID ] )
3280: {
3281: if ( [ self DoMessageInPhase ] != IO_R_SUCCESS ) /* get Identify */
3282: {
3283: PAUSE( 0, 0, 'Id -', "HandleReselectionInterrupt - Expected Identify byte after reselection.\n" );
3284: }
3285: }
3286: else return;
3287: }
3288:
3289: /* Try to find an untagged command for this Target/LUN: */
3290:
3291: ioReturn = [ self reselectNexus : gCurrentTarget
3292: lun : gCurrentLUN
3293: queueTag : 0 ];
3294:
3295: if ( ioReturn != IO_R_SUCCESS )
3296: { /* No untagged command, try to get a Tag. Hope that */
3297: /* you're still in Message-In phase at this point. */
3298: if ( [ self DoMessageInPhase ] != IO_R_SUCCESS ) /* get Tag msg */
3299: {
3300: PAUSE( 0, 0, 'tag-', "HandleReselectionInterrupt - Expected tag message.\n" );
3301: }
3302: ioReturn = [ self reselectNexus : gCurrentTarget
3303: lun : gCurrentLUN
3304: queueTag : msgInTag ];
3305: }
3306:
3307: if ( ioReturn == IO_R_SUCCESS )
3308: {
3309: ELG( gActiveCommand,
3310: (msgInTag<<16) | (gCurrentLUN<<8) | gCurrentTarget,
3311: 'Resl', "HandleReselectionInterrupt" );
3312: /* If reselectNexus succeeded, gActiveCommand is set to the command.*/
3313: /* Clear out the channel command results and build the channel */
3314: /* command to continue operation. The TRUE flag prevents */
3315: /* constructing an arbitrate/select/command sequence. */
3316:
3317: [ self ClearCPResults ];
3318: [ self UpdateCP : TRUE ];
3319: [ self RunDBDMA : kcclReselect stageLabel : kcclStageInit ];
3320: }
3321: else
3322: { /* There is no associated command. */
3323: /* Reject the reselection attempt. */
3324: /* This should cycle back to selectNextRequest. */
3325: PAUSE( gCurrentTarget, msgInTag, 'Rsl-',
3326: "HandleReselectionInterrupt - No command for reselection attempt.\n" );
3327: [ self AbortActiveCommand ];
3328: }
3329: return;
3330: }/* end HandleReselectionInterrupt */
3331:
3332:
3333: /* Validate the target's reselection byte (put on the bus before */
3334: /* reselecting us). Erase the initiator ID and convert the other */
3335: /* bit into an index. The algorithm should be faster than a */
3336: /* sequential search, but it probably doesn't matter much. */
3337: /* @return TRUE if successful (gCurrentTarget is now valid). */
3338: /* This function does not check whether there actually */
3339: /* is a command pending for this target. */
3340:
3341: - (Boolean) getReselectionTargetID
3342: {
3343: Boolean success = FALSE;
3344: register UInt8 targetID = 0;
3345: register UInt8 bitValue = 0; /* Suppress warning */
3346: register UInt8 targetBits;
3347:
3348:
3349: targetBits = meshAddr->xFIFO; /***** Read the FIFO *****/
3350: targetBits &= ~gInitiatorIDMask; /* Remove our bit */
3351: if ( targetBits )
3352: { /* Is there another bit? */
3353: bitValue = targetBits;
3354: if ( bitValue > 0x0F )
3355: {
3356: targetID += 4;
3357: bitValue >>= 4;
3358: }
3359: if ( bitValue > 0x03 )
3360: {
3361: targetID += 2;
3362: bitValue >>= 2;
3363: }
3364: if ( bitValue > 0x01 )
3365: {
3366: targetID += 1;
3367: }
3368: targetBits &= ~(1 << targetID); /* Remove the target mask */
3369: if ( targetBits == 0 )
3370: { /* Was exactly one set? */
3371: success = TRUE; /* Yes: success! */
3372: gCurrentTarget = targetID; /* Save the current target */
3373: }
3374: }
3375:
3376: if ( !success )
3377: PAUSE( targetID, targetBits, 'rsl-', "getReselectionTargetID - Expected Identify byte after reselection.\n" );
3378:
3379: return success;
3380: }/* end getReselectionTargetID */
3381:
3382:
3383: @end /* AppleMesh_SCSI(MeshInterrupt) */
3384:
3385:
3386: @implementation AppleMesh_SCSI( Mesh )
3387:
3388: /* Reusable hardware initializer function. if resetSCSIBus is TRUE, */
3389: /* this includes a SCSI reset. Handling of ioComplete of active and */
3390: /* disconnected commands must be done elsewhere. Returns IO_R_SUCCESS. */
3391:
3392: - (IOReturn) ResetMESH : (Boolean)resetSCSIBus
3393: {
3394: IOReturn ioReturn = IO_R_SUCCESS;
3395: UInt8 defaultSelectionTimeout = 25; // mlj ??? fix this value
3396: UInt8 target;
3397:
3398:
3399: /* Reset interrupts, the MESH Hardware Bus Adapter, and the DMA engine. */
3400:
3401: // [ self SetIntMask : 0 ]; /* ResetMESH clrs interruptMask */
3402: [ self SetSeqReg : kMeshResetMESH ]; /* completes quickly */
3403: [ self GetHBARegsAndClear : TRUE ]; /* clear cmdDone */
3404:
3405: dbdma_reset( DBDMA_MESH_SCSI );
3406:
3407: /* Init state variables: */
3408:
3409: gFlagIncompleteDBDMA = FALSE;
3410: // gBusState = SCS_DISCONNECTED;
3411:
3412: /* Smash all active command state (just in case): */
3413:
3414: gActiveCommand = NULL;
3415: gCurrentTarget = kInvalidTarget;
3416: gCurrentLUN = kInvalidLUN;
3417: gMsgInState = kMsgInInit;
3418: msgOutPtr = (UInt8*)CCLAddress( kcclMSGOdata );
3419:
3420: if ( resetSCSIBus )
3421: {
3422: ASSERT( gInterruptNestingLevel > 0 );
3423: meshAddr->busStatus1 = kMeshRst; /***** ASSERT RESET SIGNAL *****/
3424: SynchronizeIO();
3425: IODelay( 25 ); /* leave asserted for 25 mikes */
3426: meshAddr->busStatus1 = 0; /***** CLEAR RESET SIGNAL *****/
3427: SynchronizeIO();
3428:
3429: /* Delay for 250 msec after resetting the bus. */
3430: /* This serves two purposes: it gives the MESH time to */
3431: /* stabilize (about 10 msec is sufficient) and gives */
3432: /* some devices time to re-initialize themselves. */
3433:
3434: IOSleep( APPLE_SCSI_RESET_DELAY ); /* Give Targets time to clean up */
3435: [ self SetSeqReg : kMeshResetMESH ]; /* clear Err condition */
3436: [ self GetHBARegsAndClear : TRUE ]; /* check regs */
3437:
3438: for ( target = 0; target < SCSI_NTARGETS; target++ )
3439: {
3440: gPerTargetData[ target ].syncParms = kSyncParmsAsync;
3441: gPerTargetData[ target ].negotiateSDTR = kSyncParmsFast; // negotiate Fast
3442: }
3443: }/* end IF resetSCSIBus */
3444:
3445: meshAddr->selectionTimeOut = defaultSelectionTimeout;
3446: SynchronizeIO();
3447:
3448: return ioReturn;
3449: }/* end ResetMESH */
3450:
3451:
3452: /* Wait for an immediate (non-interrupting) command to complete. */
3453: /* Note that it spins while waiting. It is timed to prevent a buggy */
3454: /* chip or target from hanging the system. */
3455:
3456: - (IOReturn) WaitForMesh : (Boolean) clearInterrupts
3457: {
3458: ns_time_t startTime, endTime;
3459: IOReturn ioReturn = IO_R_SUCCESS;
3460: #if USE_ELG
3461: UInt8 *logp = g.evLogBufp;
3462: #endif /* USE_ELG */
3463: //#define WAIT_TIME (1000000000ULL)
3464: //#define WAIT_TIME (3000000ULL) // mlj - make it 3 milliseconds
3465: //#define WAIT_TIME 19000000 // mlj - make it 19 milliseconds for ZIP
3466: #define WAIT_TIME 250000000 // mlj - make it 250 milliseconds for SONY CD-ROM
3467:
3468:
3469: IOGetTimestamp( &startTime );
3470:
3471: for ( g.shadow.mesh.interrupt = 0; g.shadow.mesh.interrupt == 0; )
3472: {
3473: #if USE_ELG
3474: g.evLogBufp = logp; /* set back the log pointer */
3475: #endif /* USE_ELG */
3476: [ self GetHBARegsAndClear : clearInterrupts ];
3477:
3478: IOGetTimestamp( &endTime );
3479: if ( (endTime - startTime) >= WAIT_TIME )
3480: { /* It took too long! We're dead. */
3481: PAUSE( 0, 0, 'WFM-', "WaitForMesh - MESH chip does not respond to command.\n" );
3482: ioReturn = IO_R_INTERNAL;
3483: break;
3484: }
3485: }/* end FOR */
3486:
3487: return ioReturn;
3488: }/* end WaitForMesh */
3489:
3490:
3491: /* WaitForReq - spins while waiting. It is timed to prevent a buggy */
3492: /* chip or target from hanging the system. */
3493:
3494: - (IOReturn) WaitForReq /* This method is currently unused. */
3495: {
3496: ns_time_t startTime, endTime;
3497: IOReturn ioReturn = IO_R_SUCCESS;
3498:
3499:
3500: IOGetTimestamp( &startTime );
3501:
3502: g.shadow.mesh.busStatus0 = 0;
3503: while ( (g.shadow.mesh.busStatus0 & kMeshReq) == 0 )
3504: {
3505: [ self GetHBARegsAndClear : FALSE ];
3506:
3507: IOGetTimestamp( &endTime );
3508: if ( (endTime - startTime) >= 1000000000L )
3509: { /* It took too long! */
3510: PAUSE( endTime, startTime, 'WFR-', "WaitForReq - Target not in valid phase.\n" );
3511: ioReturn = IO_R_INTERNAL;
3512: break;
3513: }
3514: if ( (endTime - startTime) >= 1000000L
3515: && (g.shadow.mesh.busStatus0 & kMeshReq) == 0 )
3516: {
3517: IODelay( 1000 ); /* After 1 ms, start yielding time */
3518: }
3519: }/* end WHILE REQ not set */
3520:
3521: return ioReturn;
3522: }/* end WaitForReq */
3523:
3524:
3525: /* Send a command to the MESH chip. This may cause an interrupt. */
3526:
3527: - (void) SetSeqReg : (MeshCommand) meshCommand
3528: {
3529: ELG( (meshAddr->interruptMask<<16) | meshAddr->interrupt, meshCommand, '=Seq', "SetSeqReg" );
3530:
3531: if ( meshAddr->interruptMask & kMeshIntrCmdDone
3532: && meshCommand <= kMeshBusFreeCmd )
3533: ELG( meshAddr->interrupt, meshAddr->interruptMask, 'Trig',
3534: "SetSeqReg - may trigger interrupt.\n" );
3535:
3536: meshAddr->sequence = (UInt8)meshCommand; /***** DO IT *****/
3537: SynchronizeIO();
3538: IODelay( 1 ); /* G3 is too fast */
3539:
3540: return;
3541: }/* end SetSeqReg */
3542:
3543:
3544: /* MESH chip self-test. (Minimal: it could be extended.) */
3545: /* @return IO_R_SUCCESS if successful. */
3546:
3547: - (IOReturn) DoHBASelfTest
3548: {
3549: IOReturn ioReturn = IO_R_SUCCESS;
3550: UInt8 tempByte;
3551:
3552:
3553: ELG( gMESHPhysAddr, meshAddr, 'MESH', "DoHBASelfTest" );
3554: #ifdef CRAP
3555: if ( probe_rb( ((void*)((UInt32)gMESHPhysAddr) + kMeshMESHID) ) == 0 )
3556: {
3557: PAUSE( 0, gMESHPhysAddr, 'HBA-', "DoHBASelfTest - Invalid MESH physical address.\n" );
3558: ioReturn = IO_R_NO_DEVICE;
3559: }
3560: #else
3561: ASSERT( probe_rb( ((void*)((UInt32)gMESHPhysAddr) + kMeshMESHID) ) == 0 );
3562: #endif /* CRAP */
3563:
3564: if ( ioReturn == IO_R_SUCCESS )
3565: {
3566: tempByte = meshAddr->MESHID & 0x1f;
3567: if ( tempByte < kMeshMESHID_Value )
3568: {
3569: PAUSE( 0, tempByte, 'hba-', "DoHBASelfTest - Invalid MESH chip ID .\n" );
3570: ioReturn = IO_R_NO_DEVICE;
3571: }
3572: }
3573: return ioReturn;
3574: }/* end DoHBASelfTest */
3575:
3576:
3577: /* Start a Channel Program at the given offset */
3578: /* with the specified stage label. */
3579:
3580: - (void) RunDBDMA : (UInt32) offset stageLabel : (UInt32) stageLabel
3581: {
3582: register UInt8 intReg;
3583: ns_time_t arbEndTime, curTime;
3584:
3585:
3586: gMsgInFlag = 0; /* clear message-in flags. */
3587:
3588: CCLWord( kcclStageLabel ) = stageLabel; /* set the stage */
3589:
3590: /* Let MESH interrupt only for errors or exceptions, but not cmdDone */
3591: [ self SetIntMask : (kMeshIntrException | kMeshIntrError) ];
3592:
3593: intReg = meshAddr->interrupt;
3594: switch ( intReg )
3595: {
3596: case kMeshIntrCmdDone:
3597: if ( !gFlagReselecting ) // ??? Don't drop ACK fm MSG-IN or Sync data flows
3598: /* clear any pending command interrupts (but not reselect et al) */
3599: meshAddr->interrupt = kMeshIntrCmdDone; SynchronizeIO();
3600: /***** fall through *****/
3601: case 0:
3602: /* This is a Go: */
3603: /* Flush any CCL and related data to the CCL physical page */
3604: /* that may still be sitting in cache: */
3605: flush_cache_v( (vm_offset_t)cclLogAddr, cclLogAddrSize );
3606: //ELG( *(UInt32*)0xF3000020, 0, 'G C+', "RunDBDMA." );
3607: //ELG( 0, *(UInt32*)0xF300002C, 'G C ', "RunDBDMA." );
3608:
3609: if ( offset == kcclStart )
3610: {
3611: gFlagReselecting = FALSE;
3612: [ self SetSeqReg : kMeshArbitrateCmd ]; /* ARBITRATE */
3613:
3614: /* wait 50 mikes or cmdDone, whichever comes first: */
3615:
3616: IOGetTimestamp( &arbEndTime );
3617: arbEndTime += 50000;
3618: do
3619: {
3620: [ self GetHBARegsAndClear : FALSE ]; /* get regs without hosing */
3621: IOGetTimestamp( &curTime );
3622: }while ( !(g.shadow.mesh.interrupt & kMeshIntrCmdDone) && curTime < arbEndTime );
3623:
3624: if ( g.shadow.mesh.interrupt == kMeshIntrCmdDone )
3625: { /* No err, no exc: Arbitration won: */
3626: meshAddr->interrupt = kMeshIntrCmdDone;
3627: SynchronizeIO();
3628: [ self SetSeqReg : kMeshDisableReselect ]; /* disable reselect */
3629: offset = 0x150; // ??? fix this. Point to Select/Atn
3630: CCLWord( kcclStageLabel ) = kcclStageArb; /* set stage to Arbitrate */
3631: }/* end IF won Arbitration */
3632: else /* Arbitration not won - CAUTION - HACK AHEAD: */
3633: { /* Sometimes, MESH does not return ArbLost as it says in */
3634: /* the documentation. Instead, it waits for the winner to */
3635: /* get off the bus (usually after the 250 ms timeout) and */
3636: /* then MESH continues its arbitration. This wastes 250 ms */
3637: /* of valuable bus time. Further, IOmega's Zip drive has a */
3638: /* nasty bug whereby if its reselection is snubbed and it */
3639: /* times out, it leaves the I/O signal asserted on the bus */
3640: /* even as other activity on the bus unrelated to the Zip */
3641: /* is ongoing. */
3642: /* We don't need to hack if ArbLost is indicated correctly */
3643: /* or Reselect is indicated. If either is true, don't bother*/
3644: /* starting the DBDMA; rather, let the interrupt already */
3645: /* latched handle the situation. */
3646:
3647: if ( !(g.shadow.mesh.exception & (kMeshExcArbLost | kMeshExcResel)) )
3648: {
3649: ELG( '****', '****', 'HACK', "RunDBDMA - Arbitrate HACK." );
3650: [ self SetSeqReg : kMeshResetMESH ]; /* hack it: whack it*/
3651: [ self GetHBARegsAndClear : TRUE ]; /* get regs/preserve*/
3652: [ self SetSeqReg : kMeshEnableReselect ]; /* Let reselect again*/
3653: [ self GetHBARegsAndClear : FALSE ]; /* get regs/preserve*/
3654: if ( g.shadow.mesh.interrupt == 0 )
3655: PAUSE( 0, 0, 'Arb*', "RunDBDMA - Arbitrate/Reselect problem." );
3656: }
3657: if ( g.shadow.mesh.interrupt ) /* If Err or Exc set: */
3658: { g.intLevel |= kLevelLatched; /* set latched-interrupt flag. */
3659: return; /* let pending Int clean up. */
3660: }
3661: }/* end ELSE lost Arbitration */
3662: }/* end IF DBDMA to start at Arbitrate */
3663:
3664: [ self GetHBARegsAndClear : FALSE ]; // ??? debug: see if ACK still set
3665: ELG( 0, offset<<16 | stageLabel, 'DMA+', "RunDBDMA" );
3666: dbdma_start( DBDMA_MESH_SCSI, (dbdma_command_t*)((UInt32)cclPhysAddr + offset) );
3667: break;
3668:
3669: default: /* Err or Exc or both are set */
3670: ELG( 'Err ', 'Exc ', 'Pnd-', "RunDBDMA - interrupt probably pending (reselect?)." );
3671: [ self GetHBARegsAndClear : FALSE ]; // display without hosing
3672: }/* end SWITCH on interrupt register */
3673: return;
3674: }/* end RunDBDMA */
3675:
3676:
3677: /* Retrieve the MESH volatile register contents, */
3678: /* storing them in the global register shadow. */
3679: /* @param clearInts YES to clear MESH interrupts. */
3680:
3681: - (void) GetHBARegsAndClear : (Boolean) clearInts
3682: {
3683: register MeshRegister *mesh = meshAddr;
3684:
3685:
3686: g.shadow.mesh.interrupt = mesh->interrupt;
3687: g.shadow.mesh.error = mesh->error;
3688: g.shadow.mesh.exception = mesh->exception;
3689: g.shadow.mesh.FIFOCount = mesh->FIFOCount;
3690:
3691: g.shadow.mesh.busStatus0 = mesh->busStatus0;
3692: g.shadow.mesh.busStatus1 = mesh->busStatus1;
3693: g.shadow.mesh.transferCount1 = mesh->transferCount1;
3694: g.shadow.mesh.transferCount0 = mesh->transferCount0;
3695:
3696: g.shadow.mesh.sequence = mesh->sequence; // debugging
3697: g.shadow.mesh.interruptMask = mesh->interruptMask; // debugging
3698: g.shadow.mesh.syncParms = mesh->syncParms; // debugging
3699: g.shadow.mesh.destinationID = mesh->destinationID; // debugging
3700:
3701: ELG( g.shadow.longWord[ 0 ], g.shadow.longWord[ 1 ], clearInts ? 'Regs' : 'regs', "GetHBARegsAndClear." );
3702:
3703: if ( g.shadow.mesh.error ) // this occurs when dbdma -> Seq while reselect
3704: ELG( g.shadow.mesh.interruptMask, g.shadow.mesh.sequence, 'Err-',
3705: "GetHBARegsAndClear - MESH error detected" );
3706:
3707: /* It is possible to have the Reselected bit set in the Exception */
3708: /* register without an Exception bit in the interrupt register. */
3709: /* This may be caused by timing window where we clear the interrupt */
3710: /* register with the interrupt register instead of 0x07. */
3711: /* Handle this by faking an exception. */
3712: /* 04may98 - it is also possible to have PhaseMisMatch set in the */
3713: /* Exception register without Exception indicated in the Interrupt */
3714: /* register. This happened when a Synchronous output finished and */
3715: /* the target went to Message-In phase with Save-Data-Pointer. */
3716:
3717:
3718: if ( g.shadow.mesh.exception )
3719: g.shadow.mesh.interrupt |= kMeshIntrException;
3720:
3721: if ( clearInts && g.shadow.mesh.interrupt )
3722: {
3723: mesh->interrupt = g.shadow.mesh.interrupt;
3724: SynchronizeIO();
3725: }
3726: return;
3727: }/* end GetHBARegsAndClear */
3728:
3729:
3730: - (void) SetIntMask : (UInt8) mask
3731: {
3732: ELG( (meshAddr->interrupt<<16) | meshAddr->interruptMask, mask, 'Mask', "SetIntMask" );
3733: meshAddr->interruptMask = mask; /* enable whatever */
3734: SynchronizeIO();
3735: return;
3736: }/* end SetIntMask */
3737:
3738:
3739: - (void) AbortActiveCommand
3740: {
3741: IOReturn ioReturn;
3742:
3743:
3744: ELG( gActiveCommand, 0, '-AB*', "AbortActiveCommand" );
3745: [ self GetHBARegsAndClear : TRUE ]; /* clear possible cmdDone et al */
3746: [ self SetIntMask : 0 ]; /* Disable MESH interrupts */
3747:
3748: gMsgInFlag = 0; /* clear kFlagMsgIn_Reject et al */
3749:
3750: meshAddr->busStatus0 = kMeshAtn; /***** Raise ATN signal *****/
3751: SynchronizeIO();
3752:
3753: [ self SetSeqReg : kMeshBusFreeCmd ]; /* clear ACK */
3754: [ self WaitForMesh : TRUE ]; /* wait for PhaseMM */
3755:
3756: if ( (g.shadow.mesh.busStatus0 & (kMeshPhaseMask | kMeshReq))
3757: == (kBusPhaseMSGO | kMeshReq) )
3758: { /* this is what we want: */
3759: [ self SetSeqReg : kMeshFlushFIFO ]; /* Flush the FIFO */
3760: meshAddr->transferCount0 = 1; /* set TC low = 1 */
3761: meshAddr->transferCount1 = 0;
3762: meshAddr->busStatus0 = 0; /***** clear ATN signal *****/
3763: SynchronizeIO();
3764:
3765: /* Issue the Message Out sending the Abort on its way. */
3766: /* Note that this will cause an Unexpected-Disconnect. */
3767: [ self SetSeqReg : kMeshMessageOutCmd ]; /* drop ATN signal */
3768: meshAddr->xFIFO = kScsiMsgAbort; /* put out the Abort byte */
3769: ioReturn = [ self WaitForMesh : TRUE ]; /* wait for cmdDone */
3770: if ( ioReturn == IO_R_SUCCESS )
3771: {
3772: [ self SetSeqReg : kMeshEnableReselect ];/* bus about to go free */
3773: [ self SetIntMask : kMeshIntrMask ]; /* Enable interrupts */
3774: [ self SetSeqReg : kMeshBusFreeCmd ]; /* Clr ACK & go Bus-Free */
3775: g.intLevel |= kLevelLatched; /* set latched-int flag */
3776: return;
3777: }
3778: }/* end IF MSGO phase and REQ is set */
3779:
3780: /***** USE THE HAMMER - NUKE THE BUS: *****/
3781:
3782: ELG( 0, 0, '-AB-', "AbortActiveCommand - target refused to enter MSGO phase" );
3783: [ self ResetHardware : TRUE ];
3784: return;
3785: }/* end AbortActiveCommand */
3786:
3787:
3788: - (void) AbortDisconnectedCommand
3789: {
3790: CommandBuffer *cmdBuf;
3791: IOSCSIRequest *scsiReq;
3792: UInt8 msgByte;
3793:
3794:
3795: if ( !queue_empty( &abortCmdQ ) )
3796: {
3797: cmdBuf = (CommandBuffer*)queue_first( &abortCmdQ );
3798: scsiReq = cmdBuf->scsiReq;
3799: meshAddr->destinationID = scsiReq->target;
3800: msgOutPtr = (UInt8*)CCLAddress( kcclMSGOdata );
3801: msgByte = kScsiMsgIdentify | scsiReq->lun;
3802: *msgOutPtr++ = msgByte;
3803: if ( cmdBuf->queueTag )
3804: { /* Tagged command: */
3805: *msgOutPtr++ = kScsiMsgSimpleQueueTag;
3806: *msgOutPtr++ = cmdBuf->queueTag;
3807: *msgOutPtr++ = kScsiMsgAbortTag;
3808: ELG( cmdBuf, cmdBuf->queueTag, 'AbT-', "AbortDisconnectedCommand - Tag" );
3809: }
3810: else
3811: { /* Untagged command: */
3812: *msgOutPtr++ = kScsiMsgAbort;
3813: ELG( cmdBuf, 0, 'AbU-', "AbortDisconnectedCommand - Abort (untagged)" );
3814: }
3815: [ self SetupMsgO ]; /* Setup for Message Out phase. */
3816:
3817: [ self RunDBDMA : kcclStart stageLabel : kcclStageInit ];
3818: }
3819: return;
3820: }/* end AbortDisconnectedCommand */
3821:
3822:
3823: - (void) logTimestamp : (const char*) reason
3824: {
3825: #if DEBUG
3826: /* kMaxTimestamp should be greater than twice the expected method depth */
3827: /* since, if we dump the timestamp after it has wrapped around, we expect */
3828: /* to lose earlier entries and, hence, the shallower method starts. */
3829: #ifndef kMaxTimestampStack
3830: #define kMaxTimestampStack 64
3831: #endif
3832:
3833: TimestampDataRecord stack[ kMaxTimestampStack + 1 ]; /* Allocate one extra */
3834: UInt32 index = 0;
3835: int start;
3836: UInt32 count = 0;
3837: UInt32 maxDepth = 0;
3838: Boolean wasEnabled, unused;
3839: char work[ 8 ];
3840: struct timeval tv;
3841: ns_time_t lastEventTime;
3842: UInt32 elapsed;
3843: UInt32 sinceMethodStart;
3844:
3845:
3846: if ( reason )
3847: {
3848: IOLog( "%s: *** Log timestamp: %s\n", [ self name ], reason );
3849: }
3850:
3851: /* In case something we call causes timestamping, */
3852: /* we want to avoid getting into an infinite loop. */
3853:
3854: wasEnabled = EnableTimestamp( FALSE );
3855: lastEventTime = 0;
3856: while ( ReadTimestamp( &stack[ index ] ) )
3857: {
3858: ++count;
3859: work[0] = stack[ index ].timestampTag >> 24 & 0xFF;
3860: work[1] = stack[ index ].timestampTag >> 16 & 0xFF;
3861: work[2] = stack[ index ].timestampTag >> 8 & 0xFF;
3862: work[3] = stack[ index ].timestampTag >> 0 & 0xFF;
3863: work[4] = '\0';
3864:
3865: elapsed = (unsigned)stack[ index ].eventTime - lastEventTime;
3866: lastEventTime = stack[ index ].eventTime;
3867: ns_time_to_timeval( stack[ index ].eventTime, &tv );
3868:
3869: switch ( work[0] )
3870: {
3871: case '+': /* Entering a method */
3872: IOLog( "%s: '%s' %u.%06u %u.%03u 0.0 %d\n",
3873: [ self name ], work,
3874: tv.tv_sec, tv.tv_usec,
3875: elapsed / 1000, elapsed - ((elapsed / 1000) * 1000),
3876: stack[ index ].timestampValue );
3877: if ( index < kMaxTimestampStack )
3878: { if ( ++index > maxDepth )
3879: maxDepth = index;
3880: }
3881: break;
3882:
3883: case '=': /* Intermediate tag: find the method start */
3884: case '-': /* End of method: find the method start */
3885: sinceMethodStart = 0;
3886: for ( start = index - 1; start >= 0; --start )
3887: {
3888: if ( (stack[ start ].timestampTag & 0x00FFFFFF)
3889: == (stack[ index ].timestampTag & 0x00FFFFFF) )
3890: {
3891: sinceMethodStart = (unsigned)stack[ index ].eventTime
3892: - stack[ start ].eventTime;
3893: break;
3894: }
3895: }
3896: IOLog( "%s: '%s' %u.%06u %u.%03u %u.%03u %d\n",
3897: [ self name ], work,
3898: tv.tv_sec, tv.tv_usec,
3899: elapsed / 1000, elapsed - ((elapsed / 1000) * 1000),
3900: sinceMethodStart / 1000,
3901: sinceMethodStart - ((sinceMethodStart / 1000) * 1000),
3902: stack[ index ].timestampValue );
3903: if ( start >= 0 && work[0] == '-' )
3904: index = start; /* Pop the stack */
3905: break;
3906:
3907: default:
3908: IOLog( "%s: '%s' %u.%06u %u.%03u 0.0 %d _NoNestMark_\n",
3909: [ self name ],
3910: work,
3911: tv.tv_sec,
3912: tv.tv_usec,
3913: elapsed / 1000,
3914: elapsed - ((elapsed / 1000) * 1000),
3915: stack[ index ].timestampValue );
3916: break;
3917: }
3918: }/* end WHILE */
3919:
3920: IOLog( "%s: *** %d timestamps, %d max method depth\n",
3921: [ self name ], count, maxDepth );
3922: unused = EnableTimestamp( wasEnabled );
3923: #endif /* DEBUG */
3924: return;
3925: }/* end logTimestamp */
3926:
3927: @end /* AppleMesh_SCSI(Mesh) */
3928:
3929:
3930: @implementation AppleMesh_SCSI( Private )
3931:
3932: /* Private chip- and architecture-independent methods. */
3933:
3934: /* Pass one CommandBuffer to the IO thread; wait for completion. */
3935: /* (We are called on the client's execution thread.) */
3936: /* Normal completion status is in cmdBuf->scsiReq->driverStatus; */
3937: /* a non-zero return from this function indicates a Mach IPC error. */
3938: /* This method allocates and frees cmdBuf->cmdLock. */
3939:
3940: - (IOReturn) executeCmdBuf : (CommandBuffer*) cmdBuf
3941: {
3942: msg_header_t msg = cmdMessageTemplate;
3943: kern_return_t kernelReturn;
3944: IOReturn ioReturn = IO_R_SUCCESS;
3945:
3946:
3947: cmdBuf->flagActive = 0;
3948: cmdBuf->cmdLock = [ [ NXConditionLock alloc ] initWith : CMD_PENDING ];
3949: [ incomingCmdLock lock ];
3950: queue_enter( &incomingCmdQ, cmdBuf, CommandBuffer*, link );
3951: [ incomingCmdLock unlock ];
3952: ELG( cmdBuf, *(UInt32*)&incomingCmdQ, 'ExeC', "executeCmdBuf" );
3953:
3954: /* Create a Mach message and send it in order to wake up the IO thread: */
3955:
3956: msg.msg_remote_port = gKernelInterruptPort;
3957: kernelReturn = msg_send_from_kernel( &msg, MSG_OPTION_NONE, 0 );
3958: if ( kernelReturn != KERN_SUCCESS )
3959: {
3960: PAUSE( 0, kernelReturn, 'exe-', "executeCmdBuf - msg_send_from_kernel() error status .\n" );
3961: ioReturn = IO_R_IPC_FAILURE;
3962: }
3963: else /* Wait for IO complete: */
3964: {
3965: [ cmdBuf->cmdLock lockWhen : CMD_COMPLETE ];
3966: }
3967:
3968: [ cmdBuf->cmdLock free ];
3969: return ioReturn;
3970: }/* end executeCmdBuf */
3971:
3972:
3973: /* Abort all active and disconnected commands with specified status. */
3974: /* No hardware action. Currently used by threadResetBus and during */
3975: /* processing of a kCommandAbortRequest command. */
3976:
3977: - (void) abortAllCommands : (sc_status_t)status
3978: {
3979: ELG( 0, status, 'AbAl', "abortAllCommands" );
3980: [ incomingCmdLock lock ];
3981:
3982: [ self killActiveCommand : status ];
3983:
3984: [ self killQueue : &abortCmdQ finalStatus : status ];
3985: [ self killQueue : &disconnectedCmdQ finalStatus : status ];
3986: [ self killQueue : &pendingCmdQ finalStatus : status ];
3987: [ self killQueue : &incomingCmdQ finalStatus : status ];
3988:
3989: [ incomingCmdLock unlock ];
3990: return;
3991: }/* end abortAllCommands */
3992:
3993:
3994: /* Abort all active and disconnected commands with status SR_IOST_RESET. */
3995: /* Reset hardware and SCSI bus. */
3996: /* If there is a command in pendingCmdQ, start it up. */
3997:
3998: - (void) threadResetBus : (const char*) reason
3999: {
4000: [ self abortAllCommands : SR_IOST_RESET ];
4001: [ self ResetHardware : TRUE ]; /* Reset SCSI and chip */
4002: [ self selectNextRequest ]; /* This restarts processing commands */
4003: return;
4004: }/* end threadResetBus */
4005:
4006:
4007: /* Commence processing of the specified command. This is called by */
4008: /* commandRequestOccurred when it receives a kCommandExecute message */
4009: /* from IODirectDevice. There is a new SCSI request. Either start it */
4010: /* now, or add it to the end of our pending request queue. */
4011:
4012: - (void) threadExecuteRequest : (CommandBuffer*) cmdBuf
4013: {
4014: HardwareStartResult rc;
4015:
4016:
4017: if ( gActiveCommand || (g.intLevel & kLevelLatched) )
4018: {
4019: /* We are currently executing a request. */
4020:
4021: ELG( gActiveCommand, cmdBuf, 'Busy', "threadExecuteRequest - bus busy so queue this cmd" );
4022: queue_enter( &pendingCmdQ, cmdBuf, CommandBuffer*, link );
4023: }
4024: else if ( [ self commandCanBeStarted : cmdBuf ] == FALSE )
4025: {
4026: /* This request can't be started right now (perhaps the */
4027: /* target's tagged command limit has been reached). */
4028:
4029: ELG( cmdBuf, 0, 'qFul', "threadExecuteRequest - can't start cmd so queue this cmd" );
4030: queue_enter( &pendingCmdQ, cmdBuf, CommandBuffer*, link );
4031: }
4032: else
4033: { /* Apparently, we can start this request. Call the hardware layer. */
4034:
4035: rc = [ self hardwareStart : cmdBuf ];
4036: switch ( rc )
4037: {
4038: case kHardwareStartOK: /* Command started correctly */
4039: case kHardwareStartBusy: /* Hardware can't start now */
4040: break;
4041: case kHardwareStartRejected: /* Command rejected, try another */
4042: [ self selectNextRequest ]; /* Try another command */
4043: }
4044: }
4045: return;
4046: }/* end threadExecuteRequest */
4047:
4048:
4049: /* Called when a transaction associated with cmdBuf is complete. */
4050: /* Notify waiting thread. If cmdBuf->scsiReq exists (i.e., this */
4051: /* is not a reset or an abort), scsiReq->driverStatus must be valid.*/
4052: /* If cmdBuf is active, caller must remove from gActiveCommand. */
4053: /* We decrement activeArray[][] counter if appropriate. */
4054:
4055: - (void) ioComplete : (CommandBuffer*) cmdBuf
4056: {
4057: ns_time_t currentTime;
4058: IOSCSIRequest *scsiReq;
4059:
4060:
4061: ASSERT( cmdBuf );
4062: ELG( cmdBuf->scsiReq, cmdBuf->scsiReq->driverStatus, ' IOC', "ioComplete" );
4063:
4064: if ( cmdBuf == gActiveCommand )
4065: [ self deactivateCmd : cmdBuf ];
4066:
4067: scsiReq = cmdBuf->scsiReq;
4068: if ( scsiReq )
4069: {
4070: IOGetTimestamp( ¤tTime );
4071: scsiReq->totalTime = currentTime - cmdBuf->startTime;
4072: scsiReq->bytesTransferred = cmdBuf->currentDataIndex;
4073:
4074: /* Catch bad SCSI status now. */
4075:
4076: if ( scsiReq->driverStatus == SR_IOST_GOOD )
4077: {
4078: if ( cmdBuf->flagIsAutosense )
4079: {
4080: /* We are completing an autosense command. Don't touch */
4081: /* the request status (it should still be Check Condition). */
4082: /* Queue full is a real problem. */
4083:
4084: ASSERT( scsiReq->scsiStatus == kScsiStatusCheckCondition );
4085: switch ( cmdBuf->autosenseStatus )
4086: {
4087: case kScsiStatusGood:
4088: scsiReq->driverStatus = SR_IOST_CHKSV;
4089: break;
4090:
4091: case kScsiStatusQueueFull:
4092: if ( [ self pushbackFullTargetQueue : cmdBuf ] == SR_IOST_GOOD)
4093: {
4094: return; /* We'll try this one again */
4095: }
4096: /* Fall through to failure */
4097:
4098: default:
4099: scsiReq->driverStatus = SR_IOST_CHKSNV;
4100: break;
4101: }
4102: }
4103: else /* not AutoSense: */
4104: {
4105: switch ( scsiReq->scsiStatus )
4106: {
4107: case kScsiStatusGood:
4108: break;
4109:
4110: case kScsiStatusCheckCondition:
4111:
4112: /***** The 386 hardware suppresses autosense for ****/
4113: /***** Test Unit Ready to avoid request sense ****/
4114: /***** when polling for removable devices. This ****/
4115: /***** should be the caller's decision. ****/
4116:
4117: ELG( 0, 0, 'Chek', "ioComplete - Check Condition" );
4118: if ( gOptionAutoSenseEnable
4119: && (scsiReq->ignoreChkcond == FALSE) )
4120: {
4121: cmdBuf->flagIsAutosense = 1;/* We're doing autosense */
4122: queue_enter_first( &pendingCmdQ, cmdBuf, CommandBuffer*, link );
4123: return;
4124: }
4125: else
4126: { /* This command failed and we aren't doing autosense. */
4127: scsiReq->driverStatus = SR_IOST_CHKSNV;
4128: }
4129: break;
4130:
4131: case kScsiStatusQueueFull:
4132: if ( [ self pushbackFullTargetQueue : cmdBuf ] == SR_IOST_GOOD)
4133: {
4134: return;
4135: }
4136: /* Huh? we weren't doing tagged queuing, fall through */
4137:
4138: default:
4139: scsiReq->driverStatus = SR_IOST_BADST;
4140: break;
4141: }/* end SWITCH on SCSI status */
4142:
4143: }/* end IF driverStatus is SR_IOST_GOOD */
4144: }/* end IF not autosense */
4145: }/* end IF have scsiReq */
4146:
4147: if ( cmdBuf->flagActive )
4148: { /* Note that the active flag is false for non-kCommandExecute */
4149: /* commands and commands aborted from pendingCmdQ. */
4150:
4151: ASSERT( cmdBuf == gActiveCommand );
4152: [ self deactivateCmd : cmdBuf ];
4153: }
4154:
4155: [ cmdBuf->cmdLock lock ];
4156: [ cmdBuf->cmdLock unlockWith : YES ];
4157:
4158: return;
4159: }/* end ioComplete */
4160:
4161:
4162: /* A target reported a full queue. Push this command back */
4163: /* on the pending queue and try it again, later. */
4164: /* Return SR_IOST_GOOD if successful, SR_IOST_BADST on failure. */
4165:
4166: - (sc_status_t) pushbackFullTargetQueue : (CommandBuffer*) cmdBuf
4167: {
4168: IOSCSIRequest *scsiReq;
4169: int target, lun;
4170: IOReturn ioReturn;
4171:
4172:
4173: ASSERT( cmdBuf && cmdBuf->scsiReq );
4174: /* Avoid notifying client of this condition; update */
4175: /* perTarget.maxQueue and place this request on pendingCmdQ. */
4176: /* We'll try this again when we ioComplete at least one */
4177: /* command in this target's queue. */
4178: /* Note that this can execute commands out of order. */
4179: /* This can be disastrous for directory commands. */
4180: /* In the long run, the client (disk/tape/whatever) */
4181: /* needs to tell us how to execute the command */
4182: /* (in-order, out-of-order, etc.) For example, */
4183: /* virtual-memory page faults can be executed */
4184: /* out of order, but directory and volume bitmap */
4185: /* updates must be executed in-order to preserve */
4186: /* volume integrity. */
4187: if ( cmdBuf->queueTag == QUEUE_TAG_NONTAGGED )
4188: {
4189: /* Huh? We're not doing command queueing... */
4190: ioReturn = SR_IOST_BADST;
4191: }
4192: else
4193: {
4194: scsiReq = cmdBuf->scsiReq;
4195: target = scsiReq->target;
4196: lun = scsiReq->lun;
4197: gPerTargetData[ target ].maxQueue = gActiveArray[ target ][ lun ];
4198: [ self pushbackCurrentRequest : cmdBuf ];
4199: ioReturn = SR_IOST_GOOD;
4200: }
4201: return ioReturn;
4202: }/* end pushbackFullTargetQueue */
4203:
4204:
4205: /* Push this request back on the pending queue. */
4206:
4207: - (void) pushbackCurrentRequest : (CommandBuffer*) cmdBuf
4208: {
4209: ASSERT( cmdBuf );
4210: if ( cmdBuf->flagActive )
4211: {
4212: ASSERT( cmdBuf == gActiveCommand );
4213: [ self deactivateCmd : cmdBuf ];
4214: }
4215: queue_enter_first( &pendingCmdQ, cmdBuf, CommandBuffer*, link );
4216: return;
4217: }/* end pushbackCurrentRequest */
4218:
4219:
4220: /* Kill a request that can't be continued. */
4221:
4222: - (void) killCurrentRequest
4223: {
4224: CommandBuffer *cmdBuf;
4225: IOSCSIRequest *scsiReq;
4226:
4227:
4228: if ( gActiveCommand )
4229: {
4230: cmdBuf = gActiveCommand;
4231: ASSERT( cmdBuf->scsiReq );
4232: scsiReq = cmdBuf->scsiReq;
4233:
4234: if ( cmdBuf->flagRequestSelectOK == FALSE )
4235: scsiReq->driverStatus = SR_IOST_SELTO; /* No such device */
4236: else scsiReq->driverStatus = SR_IOST_HW; /* Target went away */
4237:
4238: [ self deactivateCmd : cmdBuf ];
4239: [ self ioComplete : cmdBuf ];
4240: }
4241: return;
4242: }/* end killCurrentRequest */
4243:
4244:
4245: /* IO associated with gActiveCommand has disconnected. */
4246: /* Place it on the disconnected command queue and */
4247: /* enable another transaction. */
4248:
4249: - (void) disconnect
4250: {
4251: ASSERT( gActiveCommand );
4252: queue_enter( &disconnectedCmdQ, gActiveCommand, CommandBuffer*, link );
4253:
4254:
4255: /* Record this time so that gActiveCommand can be billed */
4256: /* for disconnect latency at reselect time. */
4257:
4258: IOGetTimestamp( &gActiveCommand->disconnectTime );
4259: gActiveCommand = NULL;
4260: gCurrentTarget = kInvalidTarget;
4261: gCurrentLUN = kInvalidLUN;
4262:
4263: /* Since there is no active command, the caller */
4264: /* must configure the bus interface to wait for */
4265: /* bus free, then allow reselection. */
4266:
4267: return;
4268: }/* end disconnect */
4269:
4270:
4271: /* The specified target, LUN, and queueTag is trying to reselect. */
4272: /* If we have a CommandBuffer for this TLQ nexus on disconnectQ, */
4273: /* remove it, make it the current gActiveCommand, and return YES. */
4274: /* Else return NO. A value of zero for queueTag indicates a */
4275: /* nontagged command (zero is never used as the queue tag value for */
4276: /* a tagged command). */
4277:
4278: - (IOReturn) reselectNexus : (UInt8) target
4279: lun : (UInt8) lun
4280: queueTag : (UInt8) queueTag
4281: {
4282: CommandBuffer *cmdBuf;
4283: IOSCSIRequest *scsiReq;
4284: ns_time_t currentTime;
4285: IOReturn ioReturn = SR_IOST_BV; /* Presume failure */
4286:
4287:
4288: /* Scan the disconnected queue looking for */
4289: /* a command for this nexus. */
4290:
4291: ASSERT( gActiveCommand == NULL );
4292:
4293: cmdBuf = (CommandBuffer*)queue_first( &disconnectedCmdQ );
4294:
4295: while ( !queue_end( &disconnectedCmdQ, (queue_t)cmdBuf ) )
4296: {
4297: scsiReq = cmdBuf->scsiReq;
4298: if (scsiReq->target == target
4299: && scsiReq->lun == lun
4300: && cmdBuf->queueTag == queueTag )
4301: {
4302: /* We found the correct command. */
4303:
4304: queue_remove( &disconnectedCmdQ, cmdBuf, CommandBuffer*, link );
4305: gActiveCommand = cmdBuf;
4306: ASSERT( scsiReq->target == gCurrentTarget && scsiReq->lun == gCurrentLUN );
4307:
4308: /* Bill this operation for latency time: */
4309:
4310: IOGetTimestamp( ¤tTime );
4311: scsiReq->latentTime += (currentTime - gActiveCommand->disconnectTime);
4312: ioReturn = IO_R_SUCCESS;
4313: break;
4314: }
4315: /* Try next element in queue. */
4316:
4317: cmdBuf = (CommandBuffer*)cmdBuf->link.next;
4318: }/* end WHILE */
4319:
4320: return ioReturn;
4321: }/* end reselectNexus */
4322:
4323:
4324: /* Determine if gActiveArray[][], maxQueue, cmdQueueEnable, and a cmd's */
4325: /* Target and LUN show that it's OK to start processing cmdBuf. */
4326: /* Returns YES if this command can be started. */
4327: /***** Here's where we can test for a frozen LUN queue. *****/
4328:
4329: - (Boolean) commandCanBeStarted : (CommandBuffer*) cmdBuf
4330: {
4331: IOSCSIRequest *scsiReq;
4332: unsigned target;
4333: unsigned lun;
4334: UInt8 active;
4335: UInt8 maxQ;
4336: Boolean result;
4337:
4338:
4339: ASSERT( cmdBuf && cmdBuf->scsiReq );
4340:
4341: scsiReq = cmdBuf->scsiReq;
4342: target = scsiReq->target;
4343: lun = scsiReq->lun;
4344: active = gActiveArray[ target ][ lun ];
4345: if ( active == 0 )
4346: { /* No commands are active for this target, always ok. */
4347: result = TRUE;
4348: }
4349: else if ( gOptionCmdQueueEnable == FALSE
4350: || ((gPerTargetData[ target ].inquiry_7 & 0x02) == 0) )
4351: {
4352: ELG( active, gOptionCmdQueueEnable, 'CQE-', "commandCanBeStarted - cmd q'n disabled" );
4353: result = FALSE; /* q'ing is disabled for target (or disabled globally) */
4354: }
4355: else
4356: {
4357: maxQ = gPerTargetData[ target ].maxQueue;
4358: if ( maxQ == 0 || active < maxQ )
4359: { /* If maxQ is zero, we haven't reached the target's limit. */
4360: /* Otherwise, we're under the limit. */
4361: /* In both cases, we can (presumably) start this command. */
4362:
4363: result = TRUE;
4364: }
4365: else
4366: {
4367: ELG( maxQ, active, 'QLm-', "commandCanBeStarted - queue limit reached." );
4368: result = FALSE; /* We're over the target limit. Wait on this one. */
4369: }
4370: }
4371: return result;
4372: }/* end commandCanBeStarted */
4373:
4374:
4375: /* The bus has gone free. Start up a command from pendingCmdQ, */
4376: /* if any, and if allowed by cmdQueueEnable and gActiveArray[][]. */
4377: /* This is called from the interrupt routine when it is about to exit */
4378: /* (and the bus is free and there is no active command). It may also */
4379: /* be called from threadExecuteRequest when the selected command */
4380: /* couldn't be started. */
4381:
4382: - (void) selectNextRequest
4383: {
4384: CommandBuffer *cmdBuf = NULL;
4385: Boolean foundRequest = FALSE;
4386:
4387:
4388: if ( !queue_empty( &abortCmdQ ) )
4389: {
4390: [ self AbortDisconnectedCommand ];
4391: return;
4392: }
4393:
4394: if ( !queue_empty( &pendingCmdQ ) )
4395: {
4396: /* Attempt to find a CommandBuffer in pendingCmdQ */
4397: /* which we are in a position to process: */
4398:
4399: cmdBuf = (CommandBuffer*)queue_first( &pendingCmdQ );
4400: while ( !queue_end( &pendingCmdQ, (queue_entry_t)cmdBuf ) )
4401: {
4402: if ( [ self commandCanBeStarted : cmdBuf ] )
4403: {
4404: queue_remove( &pendingCmdQ, cmdBuf, CommandBuffer*, link);
4405: ELG( cmdBuf, cmdBuf->scsiReq->lun<<16 | cmdBuf->scsiReq->target, 'De Q', "selectNextRequest - dequeued one." );
4406: foundRequest = TRUE;
4407: break;
4408:
4409: /* Note that threadExecuteRequest may call selectNextRequest */
4410: /* if the command was rejected. If so, the rejected */
4411: /* command will have been returned (with an error */
4412: /* status) to its client, so there is no chance of */
4413: /* an infinite loop here. */
4414: }
4415: else
4416: {
4417: cmdBuf = (CommandBuffer*)queue_next( &cmdBuf->link );
4418: }
4419: }/* end WHILE */
4420: }/* end IF queue not empty */
4421:
4422: if ( foundRequest )
4423: [ self threadExecuteRequest : cmdBuf ];
4424:
4425: return;
4426: }/* end selectNextRequest */
4427:
4428:
4429: - (void) killActiveCommand : (sc_status_t)status
4430: {
4431: ELG( gActiveCommand, status, 'KilA', "killActiveCommand" );
4432: if ( gActiveCommand )
4433: {
4434: gActiveCommand->scsiReq->driverStatus = status;
4435: [ self ioComplete : gActiveCommand ];
4436: gActiveCommand = NULL;
4437: gCurrentTarget = kInvalidTarget;
4438: gCurrentLUN = kInvalidLUN;
4439: }
4440: return;
4441: }/* end killActiveCommand */
4442:
4443:
4444: /* Called by chip level to indicate that a command */
4445: /* has gone out to the hardware. */
4446:
4447: - (void) activateCommand : (CommandBuffer*)cmdBuf
4448: {
4449: ASSERT( gActiveCommand == NULL );
4450:
4451: /* This is the only place where a gActiveArray[][] counter */
4452: /* is incremented (and, hence, the only place where */
4453: /* cmdBuf->active is set). The only other place gActiveCommand */
4454: /* is set to non-NULL is in reselectNexus:target:lun:queueTag */
4455: /* (but that doesn't increment the active command counter) */
4456:
4457: gActiveCommand = cmdBuf;
4458: gCurrentTarget = cmdBuf->scsiReq->target;
4459: gCurrentLUN = cmdBuf->scsiReq->lun;
4460: gActiveArray[ gCurrentTarget ][ gCurrentLUN ]++;
4461: gActiveCount++;
4462:
4463: cmdBuf->flagActive = TRUE;
4464:
4465: /* Accumulate statistics. */
4466:
4467: gMaxQueueLen = MAX( gMaxQueueLen, gActiveCount );
4468: gQueueLenTotal += gActiveCount;
4469: gTotalCommands++;
4470: return;
4471: }/* end activateCommand */
4472:
4473:
4474: /* Remove specified cmdBuf from "active" status. */
4475: /* Update activeArray, activeCount, and unschedule pending timer. */
4476:
4477: - (void) deactivateCmd : (CommandBuffer*)cmdBuf
4478: {
4479: IOSCSIRequest *scsiReq;
4480: unsigned target, lun;
4481:
4482:
4483: ASSERT( cmdBuf && cmdBuf->scsiReq );
4484: ASSERT( cmdBuf == gActiveCommand ); /* ?? */
4485: gActiveCommand = NULL;
4486: gCurrentTarget = kInvalidTarget;
4487: gCurrentLUN = kInvalidLUN;
4488: scsiReq = cmdBuf->scsiReq;
4489: target = scsiReq->target;
4490: lun = scsiReq->lun;
4491:
4492: ASSERT( gActiveArray[ target ][ lun ] );
4493: gActiveArray[ target ][ lun ]--;
4494: ASSERT( gActiveCount );
4495: gActiveCount--;
4496:
4497: /* Cancel pending timeout request. */
4498: /* Commands which timed out don't have a timer request pending anymore. */
4499:
4500: if ( scsiReq->driverStatus != SR_IOST_IOTO )
4501: {
4502: IOUnscheduleFunc( serviceTimeoutInterrupt, cmdBuf );
4503: }
4504: cmdBuf->flagActive = FALSE;
4505: return;
4506: }/* end deactivateCmd */
4507:
4508:
4509: /* Kill everything in the indicated queue. Called after bus reset. */
4510:
4511: - (void) killQueue : (queue_head_t*)queuePtr finalStatus : (sc_status_t)scsiStatus
4512: {
4513: CommandBuffer *cmdBuf;
4514:
4515:
4516: ELG( 0, queuePtr, 'KilQ', "killQueue" );
4517: while ( !queue_empty( queuePtr ) )
4518: {
4519: cmdBuf = (CommandBuffer*)queue_first( queuePtr );
4520: queue_remove( queuePtr, cmdBuf, CommandBuffer*, link );
4521: cmdBuf->scsiReq->driverStatus = scsiStatus;
4522: [ self ioComplete : cmdBuf ];
4523: }
4524: return;
4525: }/* end killQueue */
4526:
4527:
4528: - (void) UpdateCurrentIndex
4529: {
4530: CommandBuffer *cmdBuf = gActiveCommand;
4531: IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
4532: UInt32 count; /* DMA transfer count */
4533: UInt32 length = g.shadow.mesh.FIFOCount;
4534: UInt8 buffer[ 16 ];
4535: UInt32 i;
4536:
4537: /* Calculate the number of bytes xferred by this channel command. */
4538: /* We don't trust the DBDMA residual count. */
4539:
4540: count = CCLWord( kcclBatchSize ); /* Our transfer count */
4541: if ( count == 0 ) /* If batch is empty, */
4542: return; /* look at nothing else.*/
4543: count -= g.shadow.mesh.transferCount1 << 8; /* MESH residual high */
4544: count -= g.shadow.mesh.transferCount0; /* MESH residual low */
4545: cmdBuf->currentDataIndex += count; /* Increment data index */
4546: if ( cmdBuf->mem )
4547: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ];
4548: CCLWord( kcclBatchSize ) = 0; /* Clear our count */
4549:
4550: /* Check the FIFO, if empty, increment the current data pointer. */
4551: /* If there is stuff in it, we have more work to do. */
4552:
4553: if ( g.shadow.mesh.FIFOCount ) /* If data in FIFO: */
4554: {
4555: if ( scsiReq->read == FALSE ) /* If Writing: */
4556: {
4557: [ self SetSeqReg : kMeshFlushFIFO ];
4558: /* We didn't write these bytes in the FIFO - adjust index */
4559: cmdBuf->currentDataIndex -= g.shadow.mesh.FIFOCount;
4560: if ( cmdBuf->mem )
4561: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ];
4562:
4563: }
4564: else /* Must be Reading: */
4565: { /* On a Read with data left in the FIFO, we must copy */
4566: /* the FIFO directly into the user's data buffer: */
4567:
4568: ELG( cmdBuf->currentDataIndex, g.shadow.mesh.FIFOCount, 'FIFO',
4569: "UpdateCurrentIndex - copy FIFO to user buffer." );
4570: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ];
4571: count = scsiReq->maxTransfer - cmdBuf->currentDataIndex;
4572: if ( count > length )
4573: count = length;
4574:
4575: /* FYI - emptying the FIFO causes cmdDone to get set. */
4576:
4577: for ( i = 0; i < count; i++ )
4578: buffer[ i ] = meshAddr->xFIFO;
4579:
4580: [ cmdBuf->mem writeToClient : buffer count : count ];
4581: cmdBuf->currentDataIndex += count;
4582: [ cmdBuf->mem setPosition : cmdBuf->currentDataIndex ];
4583: }/* end if/ELSE must be Reading */
4584: }/* end IF FIFO was not empty */
4585: ELG( 0, cmdBuf->currentDataIndex, 'UpIx', "UpdateCurrentIndex" );
4586: return;
4587: }/* end UpdateCurrentIndex */
4588:
4589:
4590: @end /* AppleMesh_SCSI( Private ) */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.