|
|
1.1 root 1: /*
2: * Copyright (c) 1993 NeXT Computer, Inc.
3: *
4: * AHAThread.m - I/O thread methods for Adaptec 1542 driver.
5: *
6: * HISTORY
7: *
8: * 13 Apr 1993 Doug Mitchell at NeXT
9: * Split off from AHAController.m.
10: */
11:
12: #import "AHAThread.h"
13: #import "AHATypes.h"
14: #import "AHAInline.h"
15: #import "AHAControllerPrivate.h"
16: #import "scsivar.h"
17: #import <driverkit/generalFuncs.h>
18: #import <driverkit/kernelDriver.h>
19: #import <kernserv/prototypes.h>
20: #import <sys/param.h>
21:
22: static void ahaTimeout(void *arg);
23:
24: #define AUTO_SENSE_ENABLE 1
25:
26: /*
27: * Template for timeout message.
28: */
29: static msg_header_t timeoutMsgTemplate = {
30: 0, // msg_unused
31: 1, // msg_simple
32: sizeof(msg_header_t), // msg_size
33: MSG_TYPE_NORMAL, // msg_type
34: PORT_NULL, // msg_local_port
35: PORT_NULL, // msg_remote_port - TO
36: // BE FILLED IN
37: IO_TIMEOUT_MSG // msg_id
38: };
39:
40: @implementation AHAController(IOThread)
41:
42: /*
43: * I/O thread version of -executeRequest:buffer:client.
44: * The approximate logic is:
45: * Build up an internal ccb describing this request
46: * Put it on the queue of pending commands
47: * Run as many pending commands as possible
48: *
49: * Returns non-zero if no ccb was available for the command. This case
50: * must be handled gracefully by the caller by enqueueing the request on
51: * commandQ.
52: */
53: - (int)threadExecuteRequest : (AHACommandBuf *)cmdBuf
54: {
55: struct ccb *ccb;
56: IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
57:
58: ddm_thr("threadExecuteRequest cmdBuf 0x%x\n", cmdBuf, 2,3,4,5);
59:
60: ccb = [self allocCcb:(scsiReq->maxTransfer ? YES : NO)];
61: if(ccb == NULL) {
62: return 1;
63: }
64: if([self ccbFromCmd:cmdBuf ccb:ccb]) {
65: /*
66: * Command reject. Error status is in
67: * cmdBuf->scsiReq->driverStatus.
68: * Notify caller and clean up.
69: */
70: [self freeCcb:ccb];
71: [cmdBuf->cmdLock lock];
72: [cmdBuf->cmdLock unlockWith:CMD_COMPLETE];
73: return 0;
74: }
75:
76: /*
77: * Make sure we'll be able to time this command out. This should be
78: * rare, so we don't particularly care about how efficient it is.
79: */
80: ccb->timeoutPort = interruptPortKern;
81: IOScheduleFunc(ahaTimeout, ccb, scsiReq->timeoutLength);
82:
83: /*
84: * Stick this command on the list of pending ones, and run them.
85: */
86: queue_enter(&pendingQ, ccb, struct ccb *, ccbQ);
87: [self runPendingCommands];
88: return 0;
89:
90: }
91:
92: /*
93: * I/O thread version of -resetSCSIBus.
94: * We also interpret this to mean we should reset the board.
95: * cmdBuf == NULL indicates a call from within the I/O thread for
96: * a reason other than -resetSCSIBus (e.g., timeout recovery).
97: */
98: - (void)threadResetBus : (AHACommandBuf *)cmdBuf
99: {
100:
101: aha_ctrl_reg_t ctrl = { 0 };
102: struct ccb *ccb;
103: queue_head_t *q;
104:
105: ddm_thr("threadResetBus\n", 1,2,3,4,5);
106:
107: /*
108: * Abort all outstanding and pending commands.
109: */
110: for(q=&outstandingQ; q!=&pendingQ; q=&pendingQ) {
111: while(!queue_empty(q)) {
112: ccb = (struct ccb *)queue_first(q);
113: queue_remove(q, ccb, struct ccb *, ccbQ);
114: if(q == &outstandingQ) {
115: ASSERT(outstandingCount != 0);
116: outstandingCount--;
117: }
118: [self commandCompleted:ccb reason:CS_Reset];
119: }
120: }
121:
122: /*
123: * Now reset the hardware.
124: */
125: aha_reset_board(ioBase, ahaBoardId);
126: aha_setup_mb_area(ioBase, ahaMbArea, ahaCcb);
127:
128: ctrl.scsi_rst = 1;
129: aha_put_ctrl(ioBase, ctrl);
130:
131: IOLog("Resetting SCSI Bus...\n");
132: IOSleep(10000);
133:
134: /*
135: * Notify caller of completion if appropriate.
136: */
137: if(cmdBuf) {
138: ddm_thr("threadResetBus: I/O complete on cmdBuf 0x%x\n",
139: cmdBuf, 2,3,4,5);
140: cmdBuf->result = SR_IOST_GOOD;
141: [cmdBuf->cmdLock lock];
142: [cmdBuf->cmdLock unlockWith:CMD_COMPLETE];
143: }
144: }
145:
146: /*
147: * Build a ccb from the specified AHACommandBuf. Returns non-zero on error
148: * (i.e., on command reject from this method). In that case, error status
149: * is in cmdBuf->scsiReq->driverStatus.
150: */
151: - (int) ccbFromCmd:(AHACommandBuf *)cmdBuf ccb:(struct ccb *)ccb
152: {
153: IOSCSIRequest *scsiReq = cmdBuf->scsiReq;
154: union cdb *cdbp = &scsiReq->cdb;
155: int cdb_ctrl;
156: vm_offset_t addr, phys;
157: vm_size_t len;
158: unsigned int pages;
159: unsigned int cmdlen;
160:
161: /*
162: * Figure out what kind of cdb we've been given
163: * and snag the ctrl byte
164: */
165: switch (SCSI_OPGROUP(cdbp->cdb_opcode)) {
166:
167: case OPGROUP_0:
168: cmdlen = sizeof (struct cdb_6);
169: cdb_ctrl = cdbp->cdb_c6.c6_ctrl;
170: break;
171:
172: case OPGROUP_1:
173: case OPGROUP_2:
174: cmdlen = sizeof (struct cdb_10);
175: cdb_ctrl = cdbp->cdb_c10.c10_ctrl;
176: break;
177:
178: case OPGROUP_5:
179: cmdlen = sizeof (struct cdb_12);
180: cdb_ctrl = cdbp->cdb_c12.c12_ctrl;
181: break;
182:
183: /*
184: * Group 6 and 7 commands allow a user-specified CDB length.
185: */
186: case OPGROUP_6:
187: if(scsiReq->cdbLength)
188: cmdlen = scsiReq->cdbLength;
189: else
190: cmdlen = sizeof (struct cdb_6);
191: cdb_ctrl = 0;
192: break;
193:
194: case OPGROUP_7:
195: if(scsiReq->cdbLength)
196: cmdlen = scsiReq->cdbLength;
197: else
198: cmdlen = sizeof (struct cdb_10);
199: cdb_ctrl = 0;
200: break;
201:
202: default:
203: scsiReq->driverStatus = SR_IOST_CMDREJ;
204: return 1;
205: }
206:
207: /*
208: * Make sure nothing unreasonable has been asked of us
209: */
210: if ((cdb_ctrl & CTRL_LINKFLAG) != CTRL_NOLINK) {
211: scsiReq->driverStatus = SR_IOST_CMDREJ;
212: return 1;
213: }
214:
215: addr = (vm_offset_t)cmdBuf->buffer;
216: len = scsiReq->maxTransfer;
217:
218: if (len > 0)
219: pages = (round_page(addr+len) - trunc_page(addr)) / PAGE_SIZE;
220: else
221: pages = 0;
222:
223: ccb->cdb = *cdbp;
224: ccb->cdb_len = cmdlen;
225:
226: ccb->data_in = scsiReq->read;
227: ccb->data_out = !scsiReq->read;
228: ccb->target = scsiReq->target;
229: ccb->lun = scsiReq->lun;
230: #if AUTO_SENSE_ENABLE
231: ccb->reqsense_len = sizeof(esense_reply_t);
232: #else AUTO_SENSE_ENABLE
233: ccb->reqsense_len = 1; /* no auto reqsense */
234: #endif AUTO_SENSE_ENABLE
235:
236: /*
237: * Note Adaptec does not support command queueing. Synchronous
238: * negotiation can only be disabled by jumper. Disconnects can
239: * not be disabled.
240: */
241:
242: ccb->cmdBuf = cmdBuf;
243: ccb->total_xfer_len = 0;
244: IOGetTimestamp(&ccb->startTime);
245:
246: /*
247: * Set up the DMA address and length. If we have more than one page,
248: * then chances are that we'll have to use scatter/gather to collect
249: * all the physical pages into a single transfer.
250: */
251: if (pages == 0) {
252: aha_put_24(0, ccb->data_addr);
253: aha_put_24(0, ccb->data_len);
254: ccb->oper = AHA_CCB_INITIATOR_RESID;
255: }
256: else if (pages == 1) {
257:
258: if(IOPhysicalFromVirtual(cmdBuf->client, addr, &phys)) {
259: IOLog("%s: Can\'t get physical address\n",
260: [self name]);
261: scsiReq->driverStatus = SR_IOST_INT;
262: return 1;
263: }
264:
265: ccb->dmaList[0] = [self createDMABufferFor:&phys
266: length:len read:scsiReq->read
267: needsLowMemory:YES limitSize:NO];
268:
269: if (ccb->dmaList[0] == NULL) {
270: [self abortDMA:ccb->dmaList length:len];
271: scsiReq->driverStatus = SR_IOST_INT;
272: return 1;
273: }
274:
275: aha_put_24(phys, ccb->data_addr);
276: aha_put_24(len, ccb->data_len);
277:
278: ccb->oper = AHA_CCB_INITIATOR_RESID;
279: ccb->total_xfer_len = len;
280: }
281: else {
282: vm_offset_t lastPhys = 0;
283: unsigned int sgEntry = 0;
284: unsigned int maxEntries = MIN(pages, AHA_SG_COUNT);
285: IOEISADMABuffer *dmaBuf = ccb->dmaList;
286:
287: for (sgEntry=0; sgEntry < maxEntries; sgEntry++) {
288: struct aha_sg *sg = &ccb->sg_list[sgEntry];
289: unsigned int thisLength;
290:
291: thisLength = MIN(len, round_page(addr+1) - addr);
292:
293: if(IOPhysicalFromVirtual(cmdBuf->client,
294: addr, &phys)) {
295: IOLog("%s: Can\'t get physical address\n",
296: [self name]);
297: [self abortDMA:ccb->dmaList
298: length:ccb->total_xfer_len];
299: scsiReq->driverStatus = SR_IOST_INT;
300: return 1;
301: }
302: *dmaBuf = [self createDMABufferFor:&phys
303: length:thisLength
304: read:scsiReq->read
305: needsLowMemory:YES limitSize:NO];
306:
307: if (*dmaBuf == NULL) {
308: [self abortDMA:ccb->dmaList
309: length:ccb->total_xfer_len];
310: scsiReq->driverStatus = SR_IOST_INT;
311: return 1;
312: }
313:
314: aha_put_24(phys, sg->addr);
315: aha_put_24(thisLength, sg->len);
316:
317: ccb->total_xfer_len += thisLength;
318:
319: addr += thisLength;
320: len -= thisLength;
321: lastPhys = phys;
322: dmaBuf++;
323: }
324:
325: if(IOPhysicalFromVirtual(IOVmTaskSelf(),
326: (unsigned)ccb->sg_list,
327: &phys)) {
328: IOLog("%s: Can\'t get physical address of ccb\n",
329: [self name]);
330: IOPanic("AHAController");
331: }
332: aha_put_24(phys, ccb->data_addr);
333: aha_put_24(sgEntry * sizeof(struct aha_sg), ccb->data_len);
334:
335: ccb->oper = AHA_CCB_INITIATOR_RESID_SG;
336: }
337:
338: return 0;
339: }
340:
341: /*
342: * If any commands pending, and the controller's queue is not full,
343: * run the new commands.
344: */
345: - runPendingCommands
346: {
347: unsigned int cmdsToRun;
348: struct ccb *ccb;
349:
350: cmdsToRun = AHA_QUEUE_SIZE - outstandingCount;
351:
352: while (cmdsToRun > 0 && !queue_empty(&pendingQ)) {
353:
354: /*
355: * Dequeue pending command and add to the outstanding queue.
356: */
357: ccb = (struct ccb *) queue_first(&pendingQ);
358: queue_remove(&pendingQ, ccb, struct ccb *, ccbQ);
359: if (!ccb)
360: break;
361:
362: queue_enter(&outstandingQ, ccb, struct ccb *, ccbQ);
363: outstandingCount++;
364:
365: /*
366: * Let 'er rip...
367: */
368: ccb->mb_out->mb_stat = AHA_MB_OUT_START;
369: aha_start_scsi(ioBase);
370:
371: /*
372: * Accumulate some simple statistics: the max queue length
373: * and enough info to compute a running average of the queue
374: * length.
375: */
376: maxQueueLen = MAX(maxQueueLen, outstandingCount);
377: queueLenTotal += outstandingCount;
378: totalCommands++;
379:
380: cmdsToRun--;
381: }
382: return self;
383: }
384:
385: /*
386: * A command is done. Figure out what happened, and notify the
387: * client appropriately. Called upon detection of I/O complete interrupt,
388: * timeout detection, or when we reset the bus and blow off pending
389: * commands.
390: */
391: - (void)commandCompleted : (struct ccb *) ccb
392: reason : (completeStatus)reason
393: {
394: ns_time_t currentTime;
395: IOSCSIRequest *scsiReq;
396: AHACommandBuf *cmdBuf = ccb->cmdBuf;
397:
398: ASSERT(cmdBuf != NULL);
399: scsiReq = cmdBuf->scsiReq;
400: ASSERT(scsiReq != NULL);
401:
402: ddm_thr("commandCompleted: ccb 0x%x cmdBuf 0x%x reason %d\n",
403: ccb, cmdBuf, reason, 4,5);
404:
405: scsiReq->scsiStatus = ccb->target_status;
406:
407: switch(reason) {
408: case CS_Timeout:
409: scsiReq->driverStatus = SR_IOST_IOTO;
410: break;
411: case CS_Reset:
412: scsiReq->driverStatus = SR_IOST_RESET;
413: break;
414: case CS_Complete:
415: switch (ccb->host_status) {
416:
417: /*
418: * Handle success and data overrun/underrun. We can handle
419: * overrun/underrun as a normal case because the controller
420: * sets the data_len field to be the actual number of bytes
421: * transferred regardless of overrun.
422: */
423: case AHA_HOST_SUCCESS:
424: case AHA_HOST_DATA_OVRUN:
425: [self completeDMA:ccb->dmaList
426: length:scsiReq->maxTransfer];
427: scsiReq->bytesTransferred = ccb->total_xfer_len -
428: aha_get_24(ccb->data_len);
429:
430: /*
431: * Everything looks good. Make sure the SCSI status byte
432: * is cool before we really say everything is hunky-dory.
433: */
434: if (scsiReq->scsiStatus == STAT_GOOD)
435: scsiReq->driverStatus = SR_IOST_GOOD;
436: else if (scsiReq->scsiStatus == STAT_CHECK) {
437: if(AUTO_SENSE_ENABLE) {
438:
439: esense_reply_t *sensePtr;
440:
441: scsiReq->driverStatus = SR_IOST_CHKSV;
442:
443: /*
444: * Sense data starts immediately after the actual
445: * cdb area we use, not an entire union cdb.
446: */
447: sensePtr = (esense_reply_t *)
448: (((char *)&ccb->cdb) + ccb->cdb_len);
449: scsiReq->senseData = *sensePtr;
450: }
451: else {
452: scsiReq->driverStatus = SR_IOST_CHKSNV;
453: }
454: }
455: else
456: scsiReq->driverStatus = ST_IOST_BADST;
457: break;
458:
459: case AHA_HOST_SEL_TIMEOUT:
460: [self abortDMA:ccb->dmaList length:scsiReq->maxTransfer];
461: scsiReq->driverStatus = SR_IOST_SELTO;
462: break;
463:
464: default:
465: IOLog("AHA interrupt: bad status %x\n", ccb->host_status);
466: [self abortDMA:ccb->dmaList length:scsiReq->maxTransfer];
467: scsiReq->driverStatus = SR_IOST_INVALID;
468: break;
469: } /* switch host_status */
470: } /* switch status */
471:
472: IOGetTimestamp(¤tTime);
473: scsiReq->totalTime = currentTime - ccb->startTime;
474: cmdBuf->result = scsiReq->driverStatus;
475:
476: /*
477: * Wake up client.
478: */
479: ddm_thr("commandCompleted: I/O complete on cmdBuf 0x%x\n",
480: cmdBuf, 2,3,4,5);
481: [cmdBuf->cmdLock lock];
482: [cmdBuf->cmdLock unlockWith:CMD_COMPLETE];
483:
484: /*
485: * Free the CCB and clean up possible pending timeout.
486: */
487: (void) IOUnscheduleFunc(ahaTimeout, ccb);
488: [self freeCcb:ccb];
489: }
490:
491: /*
492: * Alloc/free ccb's. These only come from the array ahaCcb[].
493: * If we can't find one, return NULL - caller will have to try
494: * again later.
495: */
496: - (struct ccb *)allocCcb : (BOOL)doDMA
497: {
498: struct ccb *ccb;
499: int i;
500:
501: if(numFreeCcbs == 0) {
502: ddm_thr("allocCcb: numFreeCcbs = 0\n", 1,2,3,4,5);
503: return NULL;
504: }
505:
506: /*
507: * Since numFreeCcbs is non-zero, there has to be one available
508: * in ahaCcb[].
509: */
510: ccb = ahaCcb;
511: while (ccb <= &ahaCcb[AHA_QUEUE_SIZE - 1] && ccb->in_use) {
512: ccb++;
513: }
514: if (ccb > &ahaCcb[AHA_QUEUE_SIZE - 1]) {
515: IOPanic("AHAController: out of ccbs");
516: }
517: numFreeCcbs--;
518: ccb->in_use = TRUE;
519:
520: /*
521: * Null out dmaList.
522: */
523: for(i=0; i<AHA_SG_COUNT; i++) {
524: ccb->dmaList[i] = NULL;
525: }
526:
527: /*
528: * Acquire the reentrant DMA lock. This is a nop on EISA machines.
529: *
530: * Although -reserveDMALock is reentrant for multiple threads on
531: * one device, it is *not* reentrant for one thread. Thus we should
532: * only call it if we don't already hold the lock.
533: * Also, avoid this if we're not going to do any DMA.
534: */
535: if(doDMA && (++dmaLockCount == 1)) {
536: ddm_thr("allocCcb: calling reserveDMALock\n", 1,2,3,4,5);
537: [super reserveDMALock];
538: }
539: ddm_thr("allocCcb: returning 0x%x\n", ccb, 2,3,4,5);
540:
541: return ccb;
542: }
543:
544: - (void)freeCcb : (struct ccb *)ccb
545: {
546: BOOL didDMA = (ccb->total_xfer_len ? YES : NO);
547:
548: ddm_thr("freeCcb: ccb 0x%x\n", ccb, 2,3,4,5);
549: ccb->in_use = FALSE;
550: numFreeCcbs++;
551: if(didDMA && (--dmaLockCount == 0)) {
552: ddm_thr("freeCcb: calling releaseDMALock\n",
553: 1,2,3,4,5);
554: [super releaseDMALock];
555: }
556: }
557:
558: - (void) completeDMA:(IOEISADMABuffer *) dmaList length:(unsigned int) xferLen
559: {
560: IOEISADMABuffer *buf = &dmaList[0];
561: int i;
562:
563: for (i = 0; i < AHA_SG_COUNT; i++, buf++) {
564: if(*buf) {
565: [self freeDMABuffer:*buf];
566: }
567: else {
568: return;
569: }
570: }
571: }
572:
573:
574: - (void) abortDMA:(IOEISADMABuffer *) dmaList length:(unsigned int) xferLen
575: {
576: IOEISADMABuffer *buf = &dmaList[0];
577: int i;
578:
579: for (i = 0; i < AHA_SG_COUNT; i++, buf++) {
580: if(*buf) {
581: [self abortDMABuffer:*buf];
582: }
583: else {
584: return;
585: }
586: }
587: }
588:
589: @end
590:
591:
592: /*
593: * Handle timeouts. We just send a timeout message to the I/O thread
594: * so it wakes up.
595: */
596: static void
597: ahaTimeout(void *arg)
598: {
599:
600: struct ccb *ccb = arg;
601: msg_header_t msg = timeoutMsgTemplate;
602: msg_return_t mrtn;
603:
604: if(!ccb->in_use) {
605: /*
606: * Race condition - this CCB got completed another way.
607: * No problem.
608: */
609: return;
610: }
611: msg.msg_remote_port = ccb->timeoutPort;
612: IOLog("AHA timeout\n");
613: if(mrtn = msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0)) {
614: IOLog("ahaTimeout: msg_send_from_kernel() returned %d\n",
615: mrtn);
616: }
617: }
618:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.