|
|
1.1 root 1: /*
2: * Copyright (c) 1992, 1993 NeXT Computer, Inc.
3: *
4: * Adaptec AHA-1542 SCSI controller driver.
5: *
6: * HISTORY
7: *
8: * 13 Apr 1993 Doug Mitchell at NeXT
9: * Rewrote using I/O thread which has exclusive access to hardware.
10: *
11: * 22 Nov 1992 Brian Pinkerton at NeXT
12: * Created from non-driverkit driver.
13: */
14:
15: #import <sys/types.h>
16: #import <bsd/sys/param.h>
17: #import <objc/Object.h>
18: #import <kernserv/queue.h>
19: #import <kernserv/prototypes.h>
20: #import <driverkit/return.h>
21: #import <driverkit/generalFuncs.h>
22: #import <driverkit/kernelDriver.h>
23: #import <driverkit/i386/kernelDriver.h>
24: #import <driverkit/interruptMsg.h>
25: #import <driverkit/scsiTypes.h>
26: #import <bsd/dev/scsireg.h>
27: #import "scsivar.h"
28: #import <mach/message.h>
29: #import <mach/port.h>
30: #import <mach/mach_interface.h>
31: #import <machkit/NXLock.h>
32: #import <kernserv/ns_timer.h>
33: #import <driverkit/i386/ioPorts.h>
34:
35: #import <driverkit/i386/directDevice.h>
36: #import <driverkit/i386/IOEISADeviceDescription.h>
37: #import <driverkit/IOSCSIController.h>
38: #import "AHAController.h"
39: #import "AHATypes.h"
40: #import "AHAInline.h"
41: #import "AHAThread.h"
42:
43: extern unsigned ffs(unsigned mask);
44:
45: /*
46: * Template for command message sent to the I/O thread.
47: */
48: static msg_header_t AHAMessageTemplate = {
49: 0, // msg_unused
50: 1, // msg_simple
51: sizeof(msg_header_t), // msg_size
52: MSG_TYPE_NORMAL, // msg_type
53: PORT_NULL, // msg_local_port
54: PORT_NULL, // msg_remote_port - TO
55: // BE FILLED IN
56: IO_COMMAND_MSG // msg_id
57: };
58:
59: /*
60: * Private methods implemented in this file.
61: */
62: @interface AHAController(PrivateMethods)
63: - (BOOL) probeAtPortBase : (IOEISAPortAddress) portBase;
64: - (IOReturn)executeCmdBuf : (AHACommandBuf *)cmdBuf;
65: @end
66:
67:
68: @implementation AHAController
69:
70: /*
71: * Probe, configure board, and init new instance.
72: */
73: + (BOOL)probe:deviceDescription
74: {
75: AHAController *aha = [self alloc];
76: IORange ioPort;
77:
78: ddm_init("AHAController probe\n", 1,2,3,4,5);
79: aha->ioThreadRunning = NO;
80:
81: /*
82: * Check that we have some IO Ports assigned, and probe using the
83: * first IO Port.
84: * -probeAtPortBase returns TRUE if there's an AHA Controller present.
85: */
86: if ([deviceDescription numPortRanges] < 1) {
87: IOLog("AHAController: can't determine port base!\n");
88: [aha free];
89: return NO;
90: }
91: ioPort = [deviceDescription portRangeList][0];
92: if (![aha probeAtPortBase:ioPort.start]) {
93: IOLog("Adaptec154x Not Found at port 0x%x\n", ioPort.start);
94: [aha free];
95: return NO;
96: }
97: return ([aha initFromDeviceDescription:deviceDescription] ? YES : NO);
98: }
99:
100: - initFromDeviceDescription:deviceDescription
101: {
102: unsigned Lun;
103: kern_return_t krtn;
104:
105: ddm_init("AHAController initFromDeviceDescription\n", 1,2,3,4,5);
106:
107: queue_init(&outstandingQ);
108: queue_init(&pendingQ);
109: queue_init(&commandQ);
110: commandLock = [[NXLock alloc] init];
111: outstandingCount = 0;
112: dmaLockCount = 0;
113: numFreeCcbs = AHA_QUEUE_SIZE;
114:
115: /*
116: * Note the I/O thread provided by IOSCSIController is running
117: * upon return from the following method.
118: */
119: if ([super initFromDeviceDescription:deviceDescription] == nil)
120: return [self free];
121: interruptPortKern = IOConvertPort([self interruptPort],
122: IO_KernelIOTask,
123: IO_Kernel);
124: ioThreadRunning = YES;
125:
126: /*
127: * Check the channel and irq we just found against what's in our
128: * device description. If they don't match, print a nasty warning
129: * message and fail.
130: */
131: if ([deviceDescription numChannels] < 1 ||
132: [deviceDescription channel] != config.dma_channel) {
133: IOLog("AHAController: Actual DMA Channel (%d) doesn't match "
134: "configured value (%d)!\n", config.dma_channel,
135: ([deviceDescription numChannels] ?
136: [deviceDescription channel] : 0));
137: return [self free];
138: }
139:
140: if ([deviceDescription numInterrupts] < 1 ||
141: [deviceDescription interrupt] != config.irq) {
142: IOLog("AHAController: Actual IRQ (%d) doesn't match "
143: "configured value (%d)!\n", config.irq,
144: ([deviceDescription numInterrupts] ?
145: [deviceDescription interrupt] : 0));
146: return [self free];
147: }
148:
149: /*
150: * Try to set up DMA. Set the appropriate mode on the channel, and
151: * try to enable it.
152: */
153: if ([self setTransferMode:IO_Cascade forChannel:0] != IO_R_SUCCESS ||
154: [self enableChannel:0] != IO_R_SUCCESS) {
155: IOLog("AHAController: couldn't init DMA!\n");
156: return [super free];
157: }
158:
159: /*
160: * Allocate Mailboxes and CCB's from low 16 M of memory.
161: */
162: ahaMbArea = IOMallocLow(sizeof(struct aha_mb_area));
163: ahaCcb = IOMallocLow(sizeof(struct ccb) * AHA_QUEUE_SIZE);
164:
165: /*
166: * Initialize driver data structures: set up the mailbox in/out area,
167: * and initialize the CCB queues.
168: *
169: * Note that if we fail, the call to [super free] will release (and
170: * disable) our resources (IRQ, DMA channel, portRanges).
171: */
172: if (!aha_setup_mb_area(ioBase, ahaMbArea, ahaCcb)) {
173: IOLog("AHAController: couldn't set up mailbox area!\n");
174: return [self free];
175: }
176:
177: [self resetStats];
178:
179: /*
180: * Reserve our target, enable interrupts, and go.
181: */
182: for(Lun=0; Lun<SCSI_NLUNS; Lun++) {
183: [self reserveTarget:config.scsi_id lun:Lun forOwner:self];
184: }
185:
186: [self enableAllInterrupts]; /* turn on interrupts */
187:
188: /*
189: * Set the port queue length to the maximum size.
190: */
191: krtn = port_set_backlog(task_self(), [self interruptPort],
192: PORT_BACKLOG_MAX);
193: if(krtn) {
194: IOLog("%s: error %d on port_set_backlog()\n",
195: [self name], krtn);
196: /* Oh well... */
197: }
198: [self resetSCSIBus];
199: [self registerDevice]; /* this is the last thing we do! */
200:
201: return self;
202: }
203:
204: /*
205: * This is slightly incorrect, since we can actually handle more if some of
206: * the entries in the scatter/gather list can be more than PAGE_SIZE. In
207: * practice, tho, they're never bigger, so we'll make this our max size.
208: * 18 Mar 93 dmitch - use (AHA_SG_COUNT - 1) since requests (the first
209: * and the last) can cross page boundaries.
210: */
211: - (unsigned)maxTransfer
212: {
213: return (AHA_SG_COUNT - 1) * PAGE_SIZE;
214: }
215:
216: /*
217: * kill I/O thread, free up local dynamically allocated resources,
218: * then have super release resources.
219: */
220: - free
221: {
222: AHACommandBuf cmdBuf;
223:
224: if(ioThreadRunning) {
225: cmdBuf.op = AO_Abort;
226: [self executeCmdBuf:&cmdBuf];
227: }
228: if(ahaMbArea) {
229: IOFreeLow(ahaMbArea, sizeof(struct aha_mb_area));
230: }
231: if(ahaCcb) {
232: IOFreeLow(ahaCcb, sizeof(struct ccb) * AHA_QUEUE_SIZE);
233: }
234: if(commandLock) {
235: [commandLock free];
236: }
237: return [super free];
238: }
239:
240: /*
241: * Statistics support.
242: */
243: - (unsigned int) numQueueSamples
244: {
245: return totalCommands;
246: }
247:
248:
249: - (unsigned int) sumQueueLengths
250: {
251: return queueLenTotal;
252: }
253:
254:
255: - (unsigned int) maxQueueLength
256: {
257: return maxQueueLen;
258: }
259:
260:
261: - (void)resetStats
262: {
263: totalCommands = 0;
264: queueLenTotal = 0;
265: maxQueueLen = 0;
266: }
267:
268: /*
269: * Do a SCSI command, as specified by an IOSCSIRequest. All the
270: * work is done by the I/O thread.
271: */
272: - (sc_status_t) executeRequest : (IOSCSIRequest *)scsiReq
273: buffer : (void *)buffer
274: client : (vm_task_t)client
275: {
276: AHACommandBuf cmdBuf;
277:
278: ddm_exp("executeRequest: cmdBuf 0x%x\n", &cmdBuf, 2,3,4,5);
279:
280: cmdBuf.op = AO_Execute;
281: cmdBuf.scsiReq = scsiReq;
282: cmdBuf.buffer = buffer;
283: cmdBuf.client = client;
284:
285: [self executeCmdBuf:&cmdBuf];
286:
287: ddm_exp("executeRequest: cmdBuf 0x%x complete; result %d\n",
288: &cmdBuf, cmdBuf.result, 3,4,5);
289: return cmdBuf.result;
290: }
291:
292:
293: /*
294: * Reset the SCSI bus. All the work is done by the I/O thread.
295: */
296: - (sc_status_t)resetSCSIBus
297: {
298: AHACommandBuf cmdBuf;
299:
300: ddm_exp("resetSCSIBus: cmdBuf 0x%x\n", &cmdBuf, 2,3,4,5);
301:
302: cmdBuf.op = AO_Reset;
303: [self executeCmdBuf:&cmdBuf];
304: return cmdBuf.result;
305: }
306: /*
307: * The following 6 methods are all called from the I/O thread in
308: * IODirectDevice.
309: */
310:
311: /*
312: * Called from the I/O thread when it receives an interrupt message.
313: */
314: - (void)interruptOccurred
315: {
316: struct ccb *ccb;
317: aha_intr_reg_t intr;
318: aha_mb_t *mb;
319: int i;
320:
321: ddm_thr("interruptOccurred\n", 1,2,3,4,5);
322:
323: intr = aha_get_intr(ioBase);
324: aha_clr_intr(ioBase);
325:
326: if (!intr.mb_in_full)
327: return;
328:
329: /*
330: * Find all ccb's which the controller has marked completed
331: * and commandComplete: them.
332: */
333: mb = ahaMbArea->mb_in;
334: for (i = 0; i < AHA_MB_CNT; i++, mb++) {
335: if (mb->mb_stat != AHA_MB_IN_FREE) {
336:
337: /*
338: * FIXME - need IOVirtualFromPhysical(); assume for
339: * now that we can access all physical addresses.
340: */
341: ccb = (struct ccb *)aha_get_24(mb->ccb_addr);
342: mb->mb_stat = AHA_MB_IN_FREE;
343: queue_remove(&outstandingQ, ccb, struct ccb *, ccbQ);
344: ASSERT(outstandingCount != 0);
345: outstandingCount--;
346:
347: [self commandCompleted:ccb reason:CS_Complete];
348: }
349: }
350:
351: /*
352: * Handle possible pending commands (now that we've dequeued at least
353: * one CCB).
354: */
355: [self runPendingCommands];
356:
357: /*
358: * One more thing - since we probably just freed up at least one
359: * ccb, process possible entries waiting in commandQ.
360: */
361: [self commandRequestOccurred];
362: ddm_thr("interruptOccurred: DONE\n", 1,2,3,4,5);
363: }
364:
365: /*
366: * These three should not occur; they are here as error traps. All three are
367: * called out from the I/O thread upon receipt of messages which it should
368: * not be seeing.
369: */
370: - (void)interruptOccurredAt:(int)localNum
371: {
372: IOLog("%s: interruptOccurredAt:%d\n", [self name], localNum);
373: }
374:
375: - (void)otherOccurred:(int)id
376: {
377: IOLog("%s: otherOccurred:%d\n", [self name], id);
378: }
379:
380: - (void)receiveMsg
381: {
382: IOLog("%s: receiveMsg\n", [self name]);
383:
384: /*
385: * We have to let IODirectDevice take care of this (i.e., dequeue the
386: * bogus message).
387: */
388: [super receiveMsg];
389: }
390:
391: /*
392: * Called from the I/O thread when it receives a timeout
393: * message. We send these messages ourself from ahaTimeout() in
394: * AHAThread.m.
395: */
396: - (void)timeoutOccurred
397: {
398: struct ccb *ccb, *nextCcb;
399: ns_time_t now;
400: queue_head_t *queue;
401: BOOL ccbTimedOut = NO;
402: AHACommandBuf *cmdBuf;
403: IOSCSIRequest *scsiReq;
404:
405: ddm_thr("timeoutOccurred\n", 1,2,3,4,5);
406:
407: IOGetTimestamp(&now);
408:
409: /*
410: * Scan the list of outstanding and pending commands, and time
411: * out any ones whose time is past.
412: */
413:
414: for (queue = &outstandingQ; queue != &pendingQ; queue = &pendingQ) {
415:
416: ccb = (struct ccb *) queue_first(&outstandingQ);
417: while (!queue_end(&outstandingQ, (queue_entry_t) ccb)) {
418: ns_time_t expire;
419:
420: cmdBuf = ccb->cmdBuf;
421: scsiReq = cmdBuf->scsiReq;
422: expire = ccb->startTime +
423: 1000000000ULL *
424: (unsigned long long)scsiReq->timeoutLength;
425: if (now >= expire) {
426: /*
427: * Remove ccb from the oustanding queue and
428: * complete it.
429: */
430: nextCcb = (struct ccb *) queue_next(&ccb->ccbQ);
431: queue_remove(&outstandingQ, ccb, struct ccb *, ccbQ);
432: if(queue == &outstandingQ) {
433: ASSERT(outstandingCount != 0);
434: outstandingCount--;
435: }
436: [self commandCompleted:ccb reason:CS_Timeout];
437: ccb = nextCcb;
438: ccbTimedOut = YES;
439: }
440: else {
441: ccb = (struct ccb *) queue_next(&ccb->ccbQ);
442: }
443: }
444: }
445:
446: /*
447: * Reset bus. This also completes all I/Os in outstandingQ with
448: * status CS_Reset.
449: */
450: if(ccbTimedOut) {
451: [self threadResetBus:NULL];
452: }
453: ddm_thr("timeoutOccurred: DONE\n", 1,2,3,4,5);
454: }
455:
456: /*
457: * Process all commands in commandQ. If we run out of ccb's during this
458: * method, we abort, leaving commands enqueued; these will be handled after
459: * subqueuent interrupts.
460: *
461: * This is called either as a result of an IO_COMMAND_MSG message being
462: * received by the I/O thread, or upon completion of interrupt handling. In
463: * either case, it runs in the context of the I/O thread.
464: */
465: - (void)commandRequestOccurred
466: {
467: AHACommandBuf *cmdBuf;
468:
469: ddm_thr("commandRequestOccurred: top\n", 1,2,3,4,5);
470: [commandLock lock];
471: while(!queue_empty(&commandQ)) {
472: cmdBuf = (AHACommandBuf *) queue_first(&commandQ);
473: queue_remove(&commandQ, cmdBuf, AHACommandBuf *, link);
474: [commandLock unlock];
475: switch(cmdBuf->op) {
476: case AO_Reset:
477: [self threadResetBus:cmdBuf];
478: break;
479:
480: case AO_Abort:
481: /*
482: * First notify caller of completion, then
483: * self-terminate.
484: */
485: [cmdBuf->cmdLock lock];
486: [cmdBuf->cmdLock unlockWith:CMD_COMPLETE];
487: IOExitThread();
488: /* not reached */
489:
490: case AO_Execute:
491: if([self threadExecuteRequest:cmdBuf]) {
492: /*
493: * No more CCBs available. Abort this entire
494: * method. Enqueue this request on the head
495: * of commandQ for future processing.
496: */
497: [commandLock lock];
498: queue_enter_first(&commandQ, cmdBuf,
499: AHACommandBuf *, link);
500: [commandLock unlock];
501: ddm_thr("processCommandQ: no more ccbs; "
502: "cmdBuf 0x%x\n", cmdBuf, 2,3,4,5);
503: goto out;
504:
505: }
506: }
507: [commandLock lock];
508: }
509: [commandLock unlock];
510: out:
511: ddm_thr("commandRequestOccurred: DONE\n", 1,2,3,4,5);
512: return;
513: }
514:
515:
516: @end /* methods declared in AHAController.h */
517:
518: @implementation AHAController(PrivateMethods)
519:
520: - (BOOL) probeAtPortBase:(IOEISAPortAddress) portBase
521: {
522: aha_inquiry_t inquiry;
523:
524: ddm_init("AHAController probeAtPortBase\n", 1,2,3,4,5);
525:
526: ioBase = portBase;
527: aha_reset_board(ioBase, ahaBoardId);
528:
529: /*
530: * Do an inquiry to find out the board id and other things that
531: * we won't check.
532: */
533: if (!aha_probe_cmd(ioBase, AHA_CMD_DO_INQUIRY, 0, 0,
534: (unsigned char *)&inquiry, sizeof(inquiry), TRUE)) {
535: ddm_init(" ..inquiry command failed\n", 1,2,3,4,5);
536: return FALSE;
537: }
538:
539: ahaBoardId = inquiry.board_id;
540:
541: switch (ahaBoardId) {
542:
543: case AHA_1540_16HEAD:
544: case AHA_154xB:
545: case AHA_1540_64HEAD:
546: case AHA_1640:
547: case AHA_174xA:
548: case AHA_154xC:
549: break;
550: default:
551: if (ahaBoardId < AHA_154xC) {
552: ddm_init("..bogus board ID (0x%x)\n", ahaBoardId,
553: 2,3,4,5);
554: return FALSE;
555: }
556: }
557:
558: /*
559: * Attempt to read the configuration data from the board.
560: * If this succeeds, then we have successfully probed.
561: */
562: if (!aha_probe_cmd(ioBase, AHA_CMD_GET_CONFIG, 0, 0,
563: (unsigned char *)&config, sizeof(config), TRUE)) {
564: ddm_init(" ..get config command failed\n", 1,2,3,4,5);
565:
566: return FALSE;
567: }
568:
569: /*
570: * Decode the values in the config struct.
571: */
572: config.irq = ffs((unsigned int) config.irq) + 8;
573: config.dma_channel = ffs((unsigned int) config.dma_channel) - 1;
574:
575: IOLog("Adaptec154x at port 0x%x irq %d\n",
576: portBase, config.irq);
577: return TRUE;
578: }
579:
580: /*
581: * Pass one AHACommandBuf to the I/O thread; wait for completion.
582: * Normal completion status is in cmdBuf->status; a non-zero return
583: * from this function indicates a Mach IPC error.
584: *
585: * This method allocates and frees cmdBuf->cmdLock.
586: */
587: - (IOReturn)executeCmdBuf : (AHACommandBuf *)cmdBuf
588: {
589: msg_header_t msg = AHAMessageTemplate;
590: kern_return_t krtn;
591: IOReturn rtn = IO_R_SUCCESS;
592:
593: cmdBuf->cmdLock = [[NXConditionLock alloc] initWith:CMD_PENDING];
594: [commandLock lock];
595: queue_enter(&commandQ, cmdBuf, AHACommandBuf *, link);
596: [commandLock unlock];
597:
598: /*
599: * Create a Mach message and send it in order to wake up the
600: * I/O thread.
601: */
602: msg.msg_remote_port = interruptPortKern;
603: krtn = msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0);
604: if(krtn) {
605: IOLog("%s: msg_send_from_kernel() returned %d\n",
606: [self name], krtn);
607: rtn = IO_R_IPC_FAILURE;
608: goto out;
609: }
610:
611: /*
612: * Wait for I/O complete.
613: */
614: ddm_exp("executeCmdBuf: waiting for completion on cmdBuf 0x%x\n",
615: cmdBuf, 2,3,4,5);
616: [cmdBuf->cmdLock lockWhen:CMD_COMPLETE];
617: ddm_exp("executeCmdBuf: cmdBuf 0x%x complete\n",
618: cmdBuf, 2,3,4,5);
619: out:
620: [cmdBuf->cmdLock free];
621: return rtn;
622: }
623:
624: @end /* AHAController(PrivateMethods) */
625:
626:
627:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.