|
|
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 1997 Apple Computer Inc. All Rights Reserved.
28: * @author Martin Minow mailto:[email protected]
29: * @revision 1997.02.13 Initial conversion from AMDPCSCSIDriver sources.
30: *
31: * Set tabs every 4 characters.
32: *
33: * AppleMeshHardware.m - Architecture-specific methods for Apple96 SCSI driver.
34: *
35: * Edit History
36: * 1997.02.13 MM Initial conversion from AMDPCSCSIDriver sources.
37: */
38: #import "Apple96SCSI.h"
39: #import "Apple96SCSIPrivate.h"
40: #import "Apple96Hardware.h"
41: #import "Apple96HWPrivate.h"
42: #import "Apple96ConfigKeys.h"
43: #import "Apple96Curio.h"
44: #import "Apple96CurioPublic.h"
45: #import "MacSCSICommand.h"
46: #import "bringup.h"
47: #import <driverkit/generalFuncs.h>
48: #import <driverkit/kernelDriver.h>
49: #import <driverkit/align.h>
50: #import <bsd/dev/scsireg.h>
51: // #import <mach/mach.h>
52: // #import <mach/mach_error.h>
53: #import <mach/kern_return.h>
54: #import <kernserv/prototypes.h>
55:
56: static int getConfigParam(
57: id configTable,
58: const char *paramName
59: );
60: static unsigned int GetSCSICommandLength(
61: const cdb_t *cdbPtr,
62: unsigned int defaultLength
63: );
64:
65:
66: /**
67: * These are the hardware-specific methods that are not explicitly
68: * tied to Mesh and DBDMA.
69: */
70:
71: @implementation Apple96_SCSI(Hardware)
72:
73: /**
74: * Perform architecture-specific initialization: fetch the device's
75: * bus address and interrupt port number. Also, allocate one page
76: * of memory for the channel command. (Strictly speaking, we don't
77: * need an entire page, but we can use the rest of the page for
78: * a permanent status log).
79: *
80: * @param deviceDescription Specify the device to initialize.
81: * @return self [self free] if failure.
82: */
83: - hardwareInitialization
84: : deviceDescription
85: {
86: IOReturn ioReturn = IO_R_SUCCESS;
87: id result = self;
88: kern_return_t kernelReturn;
89: UInt8 lun;
90: id configTable;
91: const char *configValue;
92: UInt8 deviceNumber;
93: UInt8 functionNumber;
94: UInt8 busNumber;
95:
96: ENTRY("Hhi hardwareInitialization");
97: ddmInit("%s: hardwareInitialization\n",
98: "Apple96SCSI",
99: 2,3,4,5
100: );
101: configTable = [deviceDescription configTable];
102: ASSERT(configTable != NULL);
103: configValue = [configTable valueForStringKey: "Bus Type"];
104: if (configValue == NULL) {
105: IOLog("%s: No bus type in configuration table\n",
106: "Apple96SCSI"
107: );
108: ioReturn = IO_R_NO_DEVICE;
109: }
110: ddmInit("Bus type: \"%s\"\n", configValue, 2, 3, 4, 5);
111: if (configValue == NULL || strcmp(configValue, "PPC") != 0) {
112: IOLog("%s: Unsupported bus type \"%s\" in configuration table\n",
113: "Apple96SCSI",
114: configValue ? configValue :"null"
115: );
116: ioReturn = IO_R_NO_DEVICE;
117: }
118: if (ioReturn == IO_R_SUCCESS) {
119: #if 0 // ** ** ** Need correct definition ** ** **
120: ioReturn = [deviceDescription getPCIDevice
121: : &deviceNumber
122: function : &functionNumber
123: bus : &busNumber];
124: #else
125: deviceNumber = 00;
126: functionNumber = 00;
127: busNumber = 00;
128: kernelReturn = 0;
129: #endif
130: if (ioReturn != IO_R_SUCCESS) {
131: IOLog("%s: Can't get PCI device information: %s\n",
132: "Apple96SCSI",
133: [self stringFromReturn : ioReturn]
134: );
135: }
136: }
137: if (ioReturn == IO_R_SUCCESS) {
138: #if 0
139: IOLog("%s: Hardware device found at"
140: " bus %u, device %u, function %u, interrupt %u\n",
141: "Apple96SCSI",
142: busNumber,
143: deviceNumber,
144: functionNumber,
145: 0
146: // [deviceDescription interrupt]
147: );
148: #endif
149: }
150: if (configValue != NULL) {
151: [configTable freeString : configValue];
152: configValue = NULL;
153: }
154: if (ioReturn == IO_R_SUCCESS) {
155: ioReturn = [self hardwareAllocateHardwareAndChannelMemory : deviceDescription];
156: }
157: if (ioReturn == IO_R_SUCCESS) {
158: /*
159: * Initialize gPerTargetData[] and the dbdma channel area.
160: */
161: [self initializePerTargetData];
162: /*
163: * All of the addresses are established. Check that the hardware
164: * is present and working.
165: */
166: ioReturn = [self hardwareChipSelfTest];
167: }
168: if (ioReturn == IO_R_SUCCESS) {
169: /*
170: * Tell the superclass to initialize our I/O thread. After this,
171: * we should be able to execute SCSI requests.
172: */
173: if ([super initFromDeviceDescription:deviceDescription] == NULL) {
174: IOLog("%s: Host Adaptor was not initialized. Fatal\n",
175: "Apple96SCSI"
176: );
177: ioReturn = IO_R_NO_DEVICE;
178: }
179: }
180: if (ioReturn == IO_R_SUCCESS) {
181: ddmInit("Init: I/O thread running\n", 1, 2, 3, 4, 5);
182: gFlagIOThreadRunning = 1;
183: /*
184: * Initialize local variables. Note that activeArray and
185: * perTarget arrays are zeroed by objc runtime.
186: */
187: queue_init(&gDisconnectedCommandQueue);
188: queue_init(&gIncomingCommandQueue);
189: queue_init(&gPendingCommandQueue);
190: gIncomingCommandLock = [[NXLock alloc] init];
191: gActiveCommand = NULL;
192: [self resetStats];
193: gOptionDisconnectEnable = ENABLE_DISCONNECT;
194: gOptionCmdQueueEnable = ENABLE_TAGGED_QUEUEING;
195: gNextQueueTag = QUEUE_TAG_NONTAGGED + 1;
196: gInitiatorID = kInitiatorIDDefault;
197: /*
198: * Initiator bus ID as a bitmask for selection.
199: */
200: gInitiatorIDMask = (1 << gInitiatorID);
201: /*
202: * Reserve the initiator ID for all LUNs.
203: */
204: for (lun = 0; lun < SCSI_NLUNS; lun++) {
205: [self reserveTarget
206: : gInitiatorID
207: lun : lun
208: forOwner : self
209: ];
210: }
211: /*
212: * get tagged command queueing, sync mode, fast mode enables from
213: * configTable.
214: */
215: #ifdef CRAP /* this requires "inspector" support: */
216: gOptionCmdQueueEnable = getConfigParam(configTable, CMD_QUEUE_ENABLE);
217: #endif /* CRAP */
218: /*
219: * Note: the external bus on PCI Mac's do not support synchronous
220: * and/or fast modes. This code should be modified to read the
221: * chip version id and configure sync/fast accordingly.
222: */
223: #if 0 /* Enable for 8100 internal bus */
224: gOptionSyncModeEnable = getConfigParam(configTable, SYNC_ENABLE);
225: gOptionFastModeEnable = getConfigParam(configTable, FAST_ENABLE);
226: #else
227: gOptionSyncModeEnable = 0;
228: gOptionFastModeEnable = 0;
229: #endif
230: gOptionExtendTiming = getConfigParam(configTable, EXTENDED_TIMING);
231: gOptionAutoSenseEnable = AUTO_SENSE_ENABLE; // from bringup.h
232: /*
233: * Get internal version of interruptPort; set the port queue
234: * length to the maximum size. It is not clear if we want
235: * to do this.
236: */
237: gKernelInterruptPort = IOConvertPort(
238: [self interruptPort],
239: IO_KernelIOTask,
240: IO_Kernel
241: );
242: #if 0 // ** ** ** Need correct header file ** ** **
243: kernelReturn = port_set_backlog(
244: task_self(),
245: [self interruptPort],
246: PORT_BACKLOG_MAX
247: );
248: if (kernelReturn != KERN_SUCCESS) {
249: IOLog("%s: warning, port_set_backlog error %d (%s)\n",
250: [self name],
251: kernelReturn,
252: mach_error_string(kernelReturn)
253: );
254: }
255: #endif
256: /*
257: * Initialize the chip and reset the bus.
258: */
259: ioReturn = [self hardwareReset : TRUE reason : NULL];
260:
261: }
262: if (ioReturn == IO_R_SUCCESS) {
263: /*
264: * OK, we're ready to roll.
265: */
266: [self enableInterrupt:0];
267: // [self enableAllInterrupts ];
268: [self registerDevice];
269: }
270: else {
271: /*
272: * Do we need to free the locks and similar?
273: */
274: [self free];
275: result = NULL;
276: }
277: RESULT(result);
278: return (result);
279: }
280:
281: /**
282: * Initialize sync mode to async for all targets at start and
283: * after bus reset.
284: */
285: - (void) initializePerTargetData
286: {
287: UInt8 target;
288:
289: ENTRY("His initializeTargetSyncMode");
290: for (target = 0; target < SCSI_NTARGETS; target++) {
291: [self renegotiateSyncMode : target];
292: gPerTargetData[ target ].inquiry_7 = 0;
293: }
294: EXIT();
295: }
296:
297: /**
298: * Initialize the synchronous data transfer state for a target.
299: */
300: - (void) renegotiateSyncMode
301: : (UInt8) target
302: {
303: ENTRY("Hrs renegotiateSyncMode");
304: EXIT();
305: }
306:
307: /**
308: * Reusable CURIO init function. This includes a SCSI reset.
309: * Handling of ioComplete of active and disconnected commands must be done
310: * elsewhere. Returns IO_R_SUCCESS if successful. This is called
311: * from a Task thread. It will disable and re-enable interrupts.
312: * Reason is for error logging.
313: */
314: - (IOReturn) hardwareReset
315: : (Boolean) resetSCSIBus
316: reason : (const char *) reason
317: {
318: ENTRY("HHr hardwareReset");
319: if (reason != NULL) {
320: IOLog("%s: Bus Reset (%s)\n",
321: [self name],
322: reason
323: );
324: }
325: /*
326: * First of all, reset the hardware.
327: */
328: [self curioHardwareReset : resetSCSIBus reason : reason];
329: /*
330: * Kill all disconnected, pending, and incoming I/O requests.
331: */
332: [self abortAllCommands : SR_IOST_RESET];
333: RESULT(IO_R_SUCCESS);
334: return (IO_R_SUCCESS);
335: }
336:
337:
338: /*
339: * Start a SCSI transaction for the specified command. ActiveCmd must be
340: * NULL. A return of kHardwareStartRejected indicates that caller may try
341: * again with another command; kHardwareStartBusy indicates a condition
342: * other than (activeCmd != NULL) which prevents the processing of the command.
343: */
344: - (HardwareStartResult) hardwareStart
345: : (CommandBuffer *) cmdBuf
346: {
347: IOSCSIRequest *scsiReq;
348: HardwareStartResult result = kHardwareStartOK;
349: cdb_t *cdbp;
350: Boolean okToDisconnect = gOptionDisconnectEnable;
351: // Boolean okToDisconnect = TRUE;
352: Boolean okToQueue = gOptionCmdQueueEnable;
353: UInt8 msgByte;
354:
355: ENTRY("Hst hardwareStart");
356: /*
357: * We do not have a current command here.
358: */
359: ASSERT(cmdBuf != NULL && cmdBuf->scsiReq != NULL);
360: scsiReq = cmdBuf->scsiReq;
361: cdbp = &scsiReq->cdb;
362: ddmChip("hardwareStart[%d.%d] cmdBuf = 0x%x opcode %s\n",
363: scsiReq->target,
364: scsiReq->lun,
365: cmdBuf,
366: IOFindNameForValue(cdbp->cdb_opcode, IOSCSIOpcodeStrings),
367: 5);
368: cmdBuf->cdbLength = GetSCSICommandLength(cdbp, scsiReq->cdbLength);
369: if (cmdBuf->cdbLength == 0) {
370: /*
371: * Failure: we can't determine the length of this command.
372: */
373: [self ioComplete
374: : cmdBuf
375: finalStatus : SR_IOST_CMDREJ
376: ];
377: ddmChip("hardwareStart rejected: invalid cdb length\n", 1, 2, 3, 4, 5);
378: result = kHardwareStartRejected;
379: }
380: if (result == kHardwareStartOK) {
381: /*
382: * Peek at the control byte (the last byte in the command).
383: */
384: msgByte = ((UInt8 *) cdbp)[cmdBuf->cdbLength - 1];
385: if ((msgByte & CTRL_LINKFLAG) != CTRL_NOLINK) {
386: /*
387: * Failure: we don't support linked commands.
388: */
389: [self ioComplete
390: : cmdBuf
391: finalStatus : SR_IOST_CMDREJ
392: ];
393: ddmChip("hardwareStart rejected: linked command\n", 1, 2, 3, 4, 5);
394: result = kHardwareStartRejected;
395: }
396: }
397: if (result == kHardwareStartOK) {
398: /*
399: * Autosense always renegotiates synchronous transfer mode.
400: * This is necessary as the target might have been reset
401: * or hit with a power-cycle.
402: */
403: if (cmdBuf->flagIsAutosense) {
404: [self renegotiateSyncMode : gCurrentTarget];
405: okToDisconnect = FALSE;
406: }
407: else {
408: /*
409: * This is a real command. Setup the user data
410: * pointers and counters and build a SCSI request
411: * channel command list.
412: *
413: * First, peek at the command for some special cases.
414: */
415: cmdBuf->queueTag = QUEUE_TAG_NONTAGGED; /* No tag just yet */
416: switch (cdbp->cdb_opcode) {
417: case kScsiCmdInquiry:
418: /*
419: * The first command SCSIDisk sends us is an Inquiry
420: * command. This never gets retried, so avoid a possible
421: * reject of a command queue tag. Avoid this hack if
422: * there are any other commands outstanding for this
423: * target/lun.
424: */
425: if (gActiveArray[scsiReq->target][scsiReq->lun] == 0) {
426: scsiReq->cmdQueueDisable = TRUE;
427: }
428: okToDisconnect = FALSE;
429: break;
430: case kScsiCmdRequestSense:
431: /*
432: * Always force sync renegotiation on any Request Sense to
433: * catch independent target power cycles.
434: * (Synch renegotiation needed should be set after all
435: * target-detected errors -- fix needed in MessageIn).
436: * Sense is always issued with disconnect disabled to
437: * maintain T/L/Q nexus.
438: *
439: * Watch it: request sense from a client is incompatible
440: * with tagged queuing.
441: */
442: [self renegotiateSyncMode : gCurrentTarget];
443: okToDisconnect = FALSE;
444: break;
445:
446: case kScsiCmdTestUnitReady:
447: case kScsiCmdReadCapacity:
448: okToDisconnect = FALSE;
449: break;
450: }
451: }
452:
453: if ( scsiReq->disconnect == FALSE ) /* If this req doesn't want */
454: okToDisconnect = FALSE; /* it, clear disconnect. */
455:
456: okToQueue &= okToDisconnect
457: && (scsiReq->cmdQueueDisable == FALSE)
458: && (gPerTargetData[ scsiReq->target ].inquiry_7 & 0x02);
459:
460: cmdBuf->flagActive = 0; /* Init flags for this command */
461: /*
462: * Make sure that the chip is stable before we try to start
463: * a request.
464: */
465: /*
466: * Currently, the only expected reason we return kHardwareStartBusy
467: * is if we have a reselect pending.
468: * ** ** ** I don't think this can happen ** ** **
469: */
470: if (gFlagBusBusy) {
471: queue_enter(&gPendingCommandQueue, cmdBuf, CommandBuffer *, link);
472: ddmChip("hardwareStart deferred: bus busy\n", 1, 2, 3, 4, 5);
473: result = kHardwareStartBusy;
474: }
475: }
476: if (result == kHardwareStartOK) {
477: if (gActiveCommand != NULL) {
478: /*
479: * This should never happen. It ensures that there are
480: * no race conditions that reselect us between the time
481: * threadExecuteRequest looked at gActiveCommand and the
482: * time we disabled interrupts.
483: */
484: queue_enter(&gPendingCommandQueue, cmdBuf, CommandBuffer *, link);
485: ddmChip("hardwareStart deferred: there is an active command\n", 1, 2, 3, 4, 5);
486: result = kHardwareStartBusy;
487: }
488: }
489: if (result == kHardwareStartOK) {
490: /*
491: * Activate this command - if we fail later, we'll de-activate it.
492: */
493: #if SERIOUS_DEBUGGING
494: /*
495: * If we're reading, preset the buffer to a known data.
496: */
497: if (cmdBuf->scsiReq != NULL
498: && cmdBuf->scsiReq->read
499: && cmdBuf->buffer != NULL
500: && (((UInt32) cmdBuf->buffer) & 0x03) == 0) {
501: int i;
502: UInt32 *ptr = (UInt32 *) cmdBuf->buffer;
503: for (i = 0; i < cmdBuf->scsiReq->maxTransfer; i += sizeof (UInt32)) {
504: *ptr++ = 0xDEADBEEF;
505: }
506: }
507: #endif /* SERIOUS_DEBUGGING */
508: #if SERIOUS_DEBUGGING || LOG_COMMAND_AND_RESULT
509: [self logCommand
510: : cmdBuf
511: logMemory : FALSE
512: reason : "Command at commandStart"
513: ];
514: #endif /* SERIOUS_DEBUGGING || LOG_COMMAND_AND_RESULT */
515: ASSERT(gActiveCommand == NULL);
516: [self activateCommand : cmdBuf];
517: ASSERT(scsiReq->target == gCurrentTarget && scsiReq->lun == gCurrentLUN);
518: /* MESH only: [self clearChannelCommandResults]; */
519: /*
520: * Reset the message out buffer pointers.
521: */
522: gMsgOutPtr = gMsgOutBuffer;
523: gMsgPutPtr = gMsgOutBuffer;
524: msgByte = kScsiMsgIdentify | scsiReq->lun;
525: if (okToDisconnect)
526: msgByte |= kScsiMsgEnableDisconnectMask;
527: *gMsgOutPtr++ = msgByte;
528: /*
529: * According to the SCSI Spec, the tag command immediately
530: * follows the selection.
531: */
532: /*
533: * Note that autosense is issued on the current tag.
534: * The command was initialized with QUEUE_TAG_NONTAGGED.
535: */
536: /***** Driver Kit supports only simple queue tags. *****/
537: if ( okToQueue ) {
538: /* Avoid using tag QUEUE_TAG_NONTAGGED (zero). */
539: cmdBuf->queueTag = gNextQueueTag;
540: if (++gNextQueueTag == QUEUE_TAG_NONTAGGED) {
541: gNextQueueTag++;
542: }
543: *gMsgOutPtr++ = kScsiMsgSimpleQueueTag;
544: *gMsgOutPtr++ = cmdBuf->queueTag;
545: }
546: if (cmdBuf->flagIsAutosense == 0) {
547: cmdBuf->currentDataIndex = 0;
548: cmdBuf->savedDataIndex = 0;
549: if (gActiveCommand->mem != NULL) {
550: [gActiveCommand->mem setPosition : 0]; /* Needed? */
551: }
552: scsiReq->driverStatus = SR_IOST_INVALID;
553: scsiReq->totalTime = 0ULL;
554: scsiReq->latentTime = 0ULL;
555: }
556: [self hardwareStartSCSIRequest];
557: IOGetTimestamp(&cmdBuf->startTime);
558: }
559: RESULT(result);
560: return (result);
561: }
562:
563:
564: @end /* Apple96_SCSI(Hardware) */
565:
566:
567:
568: /*
569: * Obtain a YES/NO type parameter from the config table.
570: * @param configTable The table to examine.
571: * @param paramName The parameter to look for.
572: * @result Zero if missing from the table or the table value is
573: * not YES. One if present in the table and the table
574: * value is YES.
575: */
576: static int getConfigParam(
577: id configTable,
578: const char *paramName
579: )
580: {
581: const char *value;
582: int result = 0; // default if not present in table
583:
584: ENTRY("Hgc getConfigParam");
585: value = [configTable valueForStringKey:paramName];
586: if (value != NULL) {
587: if (strcmp(value, "YES") == 0) {
588: result = 1;
589: }
590: [configTable freeString:value];
591: }
592: RESULT(result);
593: return (result);
594: }
595:
596: static unsigned int
597: GetSCSICommandLength(
598: const cdb_t *cdbPtr,
599: unsigned int defaultLength
600: )
601: {
602: unsigned int result;
603:
604: /*
605: * Warning: don't use sizeof here - the compiler rounds the
606: * value up to the next word boundary.
607: */
608: switch (((UInt8 *) cdbPtr)[0] & 0xE0) {
609: case (0 << 5):
610: result = 6;
611: break;
612: case (1 << 5):
613: case (2 << 5):
614: result = 10;
615: break;
616: case (5 << 5):
617: result = 12;
618: break;
619: case (6 << 5):
620: result = (defaultLength != 0) ? defaultLength : 6;
621: break;
622: case (7 << 5):
623: result = (defaultLength != 0) ? defaultLength : 10;
624: break;
625: default:
626: result = 0;
627: break;
628: }
629: return (result);
630: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.