|
|
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: /* Sym8xxClient.m created by russb2 on Sat 30-May-1998 */
26:
27: #import "Sym8xxController.h"
28:
29: static u_int8_t xferMsgSync[] = {0x01, 0x03, 0x01, 0x0c, 0x10};
30: static u_int8_t xferMsgAsync[] = {0x01, 0x03, 0x01, 0x00, 0x00};
31: static u_int8_t xferMsgWide16[] = {0x01, 0x02, 0x03, 0x01};
32: static u_int8_t cdbLength[8] = { 6, 10, 10, 0, 0, 12, 0, 0 };
33:
34: @implementation Sym8xxController(Client)
35:
36: /*-----------------------------------------------------------------------------*
37: * Client thread routines.
38: *
39: * This module processes I/O requests from driverKit. It does most of the resource
40: * allocation and command preparation. Once a command is prepared it is queued
41: * to the driver's I/O Thread for execution.
42: *
43: *-----------------------------------------------------------------------------*/
44: - (sc_status_t) executeRequest:(IOSCSIRequest *)scsiReq buffer:(void *)buffer client:(vm_task_t)client
45: {
46: SRB *srb;
47: Nexus *nexus, *nexusPhys;
48: u_int32_t len;
49: ns_time_t startTime, endTime;
50:
51: IOGetTimestamp( &startTime );
52:
53: /*
54: * If a SCSI Bus reset is detected, we hold-off command processing until the targets have
55: * had a chance to recover.
56: */
57: while ( resetQuiesceTimer )
58: {
59: [resetQuiesceSem lock];
60: }
61: [resetQuiesceSem unlock];
62:
63: /*
64: * Allocate and initialize a SRB structure.
65: * Note: This routine clears the SRB and initializes srb->srbPhys
66: * which contains the physical address of the srb.
67: */
68: srb = [self Sym8xxAllocSRB];
69: if ( srb == NULL )
70: {
71: return -1;
72: }
73:
74: nexus = &srb->nexus;
75: nexusPhys = &srb->srbPhys->nexus;
76:
77: /*
78: * Set client data buffer pointers in the SRB
79: */
80: srb->xferClient = client;
81: srb->xferBuffer = (vm_offset_t) buffer;
82: srb->xferCount = scsiReq->maxTransfer;
83:
84: /*
85: * Set request sense buffer pointers in the SRB
86: */
87: if ( !scsiReq->ignoreChkcond )
88: {
89: srb->senseData = (vm_offset_t) &scsiReq->senseData;
90: srb->senseDataLength = sizeof(esense_reply_t);
91: }
92:
93: srb->srbCmd = ksrbCmdExecuteReq;
94: srb->srbState = ksrbStateCDBDone;
95:
96: srb->target = scsiReq->target;
97: srb->lun = scsiReq->lun;
98:
99: /*
100: * Setup timeout. (250ms ticks)
101: */
102: if ( scsiReq->timeoutLength )
103: {
104: srb->srbTimeout = (scsiReq->timeoutLength * 1000) / kSCSITimerIntervalMS + 1;
105: }
106:
107: srb->directionMask = (scsiReq->read) ? 0x01000000 : 0x00000000;
108:
109: /*
110: * Setup the Nexus struct. This part of the SRB is read/written both by the
111: * script and the driver.
112: */
113: nexus->targetParms.target = srb->target;
114:
115: nexus->cdb.ppData = EndianSwap32((u_int32_t)&nexusPhys->cdbData);
116:
117: len = cdbLength[scsiReq->cdb.cdb_opcode >> 5];
118: if ( len == 0 ) len = scsiReq->cdbLength;
119:
120: nexus->cdb.length = EndianSwap32( len );
121: nexus->cdbData = scsiReq->cdb;
122:
123: /*
124: * Setup SCSI Messages to send on inital selection of the target.
125: * Note: A SCSI tag for command-queuing requests is allocated
126: * when messages are generated.
127: */
128: srb->srbRequestFlags |= (scsiReq->disconnect) ? ksrbRFDisconnectAllowed : 0;
129: srb->srbRequestFlags |= (!scsiReq->syncDisable) ? ksrbRFXferSyncAllowed : 0;
130: srb->srbRequestFlags |= (!scsiReq->cmdQueueDisable) ? ksrbRFCmdQueueAllowed : 0;
131:
132: [self Sym8xxCalcMsgs:srb];
133:
134: /*
135: * Setup initial data transfer list (SGList)
136: */
137: nexus->ppSGList = (SGEntry *)EndianSwap32((u_int32_t)&nexusPhys->sgListData[2]);
138: [self Sym8xxUpdateSGList: srb ];
139:
140: /*
141: * Queue command to I/O Thread and wait for completion.
142: */
143: [self Sym8xxSendCommand: srb];
144:
145: /*
146: * If the command timed-out then issue a Maibox abort to clear
147: * the request from the target.
148: *
149: * Note: We lock the abortBdrSem to insure there is only one abort
150: * active at a time.
151: */
152: if ( srb->srbCmd == ksrbCmdProcessTimeout )
153: {
154: [abortBdrSem lock];
155: srb->srbCmd = ksrbCmdAbortReq;
156: [self Sym8xxSendCommand: srb];
157: [abortBdrSem unlock];
158: }
159:
160: /*
161: * Release the tag for the request.
162: */
163: [self Sym8xxFreeTag: srb];
164:
165: /*
166: * Transfer final request status from the SRB to the original request
167: */
168: IOGetTimestamp( &endTime );
169: scsiReq->totalTime = endTime - startTime;
170: scsiReq->driverStatus = srb->srbSCSIResult;
171: scsiReq->scsiStatus = srb->srbSCSIStatus;
172: scsiReq->bytesTransferred = srb->xferDone;
173:
174: [self Sym8xxFreeSRB: srb];
175:
176: return scsiReq->driverStatus;
177: }
178:
179: /*-----------------------------------------------------------------------------*
180: * Requests from Blue Box.
181: *
182: * Note: Hopefully this kludge of having multiple variants of executeRequest
183: * will go away soon!
184: *-----------------------------------------------------------------------------*/
185: - (sc_status_t) executeRequest : (IOSCSIRequest *) scsiReq
186: ioMemoryDescriptor : (IOMemoryDescriptor *) ioMemoryDescriptor
187: {
188: return [self executeRequest:scsiReq buffer:(void *)ioMemoryDescriptor client:(vm_task_t) -1];
189: }
190:
191:
192: /*-----------------------------------------------------------------------------*
193: * This routine queues an SRB to reset the SCSI Bus
194: *
195: *-----------------------------------------------------------------------------*/
196: - (sc_status_t) resetSCSIBus
197: {
198: SRB *srb;
199: sc_status_t scsiResult;
200:
201: srb = [self Sym8xxAllocSRB];
202: if ( srb == NULL )
203: {
204: return -1;
205: }
206:
207: srb->srbCmd = ksrbCmdResetSCSIBus;
208: [self Sym8xxSendCommand: srb];
209:
210: scsiResult = srb->srbSCSIResult;
211: [self Sym8xxFreeSRB: srb];
212:
213: return scsiResult;
214: }
215:
216:
217: /*-----------------------------------------------------------------------------*
218: * This routine queues a command on the driver's I/O Thread, wakes up
219: * the I/O Thread and then waits for the command to complete.
220: *
221: *-----------------------------------------------------------------------------*/
222: - (void) Sym8xxSendCommand: (SRB *) srb
223: {
224: kern_return_t kr;
225:
226: msg_header_t msg =
227: {
228: 0, // msg_unused
229: 1, // msg_simple
230: sizeof(msg_header_t), // msg_size
231: MSG_TYPE_NORMAL, // msg_type
232: PORT_NULL, // msg_local_port
233: PORT_NULL, // msg_remote_port - TO BE FILLED IN
234: IO_COMMAND_MSG // msg_id
235: };
236:
237: srb->srbCmdLock = [[NXConditionLock alloc] initWith: ksrbCmdPending];
238:
239: [srbPendingQLock lock];
240: queue_enter( &srbPendingQ, srb, SRB *, srbQ );
241: [srbPendingQLock unlock];
242:
243: msg.msg_remote_port = interruptPortKern;
244: kr = msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0);
245: if( kr != KERN_SUCCESS )
246: {
247: goto executeCmd_error;
248: }
249:
250: [srb->srbCmdLock lockWhen: ksrbCmdComplete];
251: [srb->srbCmdLock free];
252:
253: executeCmd_error:
254: ;
255: return;
256: }
257:
258:
259: /*-----------------------------------------------------------------------------*
260: * This routine provides our data alignment/length restrictions to the
261: * super class.
262: *
263: *-----------------------------------------------------------------------------*/
264: - (void)getDMAAlignment:(IODMAAlignment *)alignment
265: {
266: alignment->readStart = 1;
267: alignment->writeStart = 1;
268: alignment->readLength = 1;
269: alignment->writeLength = 2;
270: }
271:
272: /*-----------------------------------------------------------------------------*
273: * This routine returns the number of targets we support.
274: *
275: *-----------------------------------------------------------------------------*/
276: - (int) numberOfTargets
277: {
278: return MAX_SCSI_TARGETS;
279: }
280:
281: /*-----------------------------------------------------------------------------*
282: * This routine creates SCSI messages to send during the initial connection
283: * to the target. It is called during client request processing and also by
284: * the I/O thread when a request sense operation is required.
285: *
286: * Outbound messages are setup in the MsgOut buffer in the Nexus structure of
287: * the SRB.
288: *
289: *-----------------------------------------------------------------------------*/
290: - (void) Sym8xxCalcMsgs: (SRB *)srb
291: {
292: Nexus *nexus;
293: Nexus *nexusPhys;
294: u_int32_t msgIndex;
295: BOOL fCmdQueue;
296: BOOL fNegotiateSync;
297: BOOL fNegotiateWide;
298: u_int32_t targetFlags;
299: u_int32_t reqFlags;
300: u_int8_t *xferMsg = NULL;
301:
302: nexus = &srb->nexus;
303: nexusPhys = &srb->srbPhys->nexus;
304:
305: reqFlags = srb->srbRequestFlags;
306:
307: /*
308: * Setup Identify message
309: */
310: msgIndex = 0;
311: nexus->msg.ppData = EndianSwap32((u_int32_t)&nexusPhys->msgData);
312: nexus->msgData[msgIndex++] = srb->lun | (( reqFlags & ksrbRFDisconnectAllowed ) ? 0xC0 : 0x80);
313:
314: targetFlags = targets[srb->target].flags;
315:
316: /*
317: * Setup Tag message if cmdQueueing is supported.
318: *
319: * Note: On target flags:
320: * kTFxxxxSupported - Inquiry data indicates the function is supported.
321: * kTFxxxxAllowed - The function is not explicity disabled for this target.
322: * kRFxxxxAllowed - The function is not explicitly disabled by the command
323: */
324: fCmdQueue = ( (targetFlags & kTFCmdQueueSupported)
325: && (targetFlags & kTFCmdQueueAllowed)
326: && (reqFlags & ksrbRFCmdQueueAllowed) );
327:
328: /*
329: * Allocate tag for request.
330: *
331: * For non-tagged requests a pseudo-tag is created consisting of target*16+lun. For tagged
332: * requests a tag in the range 128-255 is allocated.
333: *
334: * If a pseudo-tag is inuse for a non-tagged command or there are no tags available for
335: * a tagged request, then the command is blocked until a tag becomes available.
336: *
337: * Note: If we are being called during request sense processing (srbState != ksrbStateCDBDone)
338: * then a tag has already been allocated to the request.
339: */
340: if ( srb->srbState == ksrbStateCDBDone )
341: {
342: srb->tag = srb->nexus.tag = [self Sym8xxAllocTag:(SRB *)srb CmdQueue:(BOOL)fCmdQueue];
343: }
344:
345: if ( fCmdQueue )
346: {
347: nexus->msgData[msgIndex++] = 0x20;
348: nexus->msgData[msgIndex++] = srb->nexus.tag;
349: }
350:
351: /*
352: * Setup to negotiate for Wide (16-bit) data transfers
353: *
354: * Note: There is no provision to negotiate back to narrow transfers although
355: * SCSI does support this.
356: */
357: fNegotiateWide = (targetFlags & kTFXferWide16Supported)
358: && (targetFlags & kTFXferWide16Allowed)
359: && !(targetFlags & kTFXferWide16);
360:
361: if ( fNegotiateWide )
362: {
363: srb->srbRequestFlags |= ksrbRFNegotiateWide;
364: bcopy( xferMsgWide16, &nexus->msgData[msgIndex], sizeof(xferMsgWide16) );
365: msgIndex += sizeof(xferMsgWide16);
366: }
367:
368: /*
369: * Setup to negotiate for Synchronous data transfers.
370: *
371: * Note: We can negotiate back to async based on the flags in the command.
372: */
373:
374: fNegotiateSync = (targetFlags & kTFXferSyncSupported)
375: && (targetFlags & kTFXferSyncAllowed)
376: && ( ((reqFlags & ksrbRFXferSyncAllowed) != 0) ^ ((targetFlags & kTFXferSync) != 0) ) ;
377:
378: if ( fNegotiateSync )
379: {
380: srb->srbRequestFlags |= ksrbRFNegotiateSync;
381: xferMsg = (reqFlags & ksrbRFXferSyncAllowed) ? xferMsgSync : xferMsgAsync;
382: bcopy( xferMsg, &nexus->msgData[msgIndex], sizeof(xferMsgSync) );
383: msgIndex += sizeof(xferMsgSync);
384: }
385:
386: /*
387: * If we are negotiating for both Sync and Wide data transfers, we setup both messages
388: * in the Nexus msgOut buffer. However, after each message the script needs to wait for
389: * a reply message from the target. In this case, we set the msgOut length to include
390: * bytes upto the end of the Wide message. When we get the reply from the target, the
391: * routine handling the WDTR will setup the Nexus pointers/counts to send the remaining
392: * message bytes. See Sym8xxExecute.m(Sym8xxNegotiateWDTR).
393: */
394: srb->srbMsgLength = msgIndex;
395:
396: if ( fNegotiateSync && fNegotiateWide ) msgIndex -= sizeof(xferMsgSync);
397:
398: nexus->msg.length = EndianSwap32( msgIndex );
399: }
400:
401: /*-----------------------------------------------------------------------------*
402: * This routine sets up the data transfer SG list for the client's buffer in the
403: * Nexus structure.
404: *
405: * The SGList actually consists of script instructions. The script will branch
406: * to the SGList when the target enters data transfer phase. When the SGList completes
407: * it will either execute a script INT instruction if there are more segments of the
408: * user buffer that need to be transferred or will execute a script RETURN instruction
409: * to return to the script.
410: *
411: * The first two slots in the SGList are reserved for partial data transfers. See
412: * Sym8xxExecute.m(Sym8xxAdjustDataPtrs).
413: *
414: *-----------------------------------------------------------------------------*/
415: - (BOOL) Sym8xxUpdateSGList: (SRB *) srb
416: {
417: BOOL rc;
418:
419: if ( srb->xferClient != (vm_task_t)-1 )
420: {
421: rc = [self Sym8xxUpdateSGListVirt: srb];
422: }
423: else
424: {
425: rc = [self Sym8xxUpdateSGListDesc: srb];
426: }
427: return rc;
428: }
429:
430: /*-----------------------------------------------------------------------------*
431: * Build SG list based on a single virtual address range/length
432: *
433: *-----------------------------------------------------------------------------*/
434: - (BOOL) Sym8xxUpdateSGListVirt: (SRB *) srb
435: {
436: u_int32_t offset;
437: u_int32_t physAddr;
438: u_int32_t bytesLeft;
439: u_int32_t bytesOnPage;
440: u_int32_t i;
441: u_int32_t len = 0;
442: IOReturn rc = IO_R_SUCCESS;
443:
444: offset = srb->xferOffset;
445: bytesLeft = srb->xferCount - srb->xferOffset;
446: i = 2;
447:
448: while ( (bytesLeft > 0) && (i < MAX_SGLIST_ENTRIES-1))
449: {
450:
451: rc = IOPhysicalFromVirtual( (vm_task_t) srb->xferClient,
452: (vm_address_t) (srb->xferBuffer+offset),
453: (u_int32_t *) &physAddr );
454:
455: if ( rc != IO_R_SUCCESS )
456: {
457: break;
458: }
459:
460: /*
461: * Note: The script instruction(s) to transfer data to/from the scsi bus
462: * have the same format as a typical SGList with the transfer length
463: * as the first word and the physical transfer address as the second.
464: * The data transfer direction is specified by a bit or'd into the
465: * high byte of the SG entry's length field.
466: */
467: srb->nexus.sgListData[i].physAddr = EndianSwap32( physAddr );
468:
469: bytesOnPage = page_size - ((srb->xferBuffer + offset) & (page_size - 1));
470: len = ( bytesLeft < bytesOnPage ) ? bytesLeft : bytesOnPage;
471:
472: srb->nexus.sgListData[i].length = EndianSwap32( len | srb->directionMask );
473:
474: bytesLeft -= len;
475: offset += len;
476: i++;
477: }
478:
479: if ( !bytesLeft )
480: {
481: srb->nexus.sgListData[i].length = EndianSwap32( 0x90080000 );
482: srb->nexus.sgListData[i].physAddr = EndianSwap32( 0x00000000 );
483: }
484: else
485: {
486: srb->nexus.sgListData[i].length = EndianSwap32( 0x98080000 );
487: srb->nexus.sgListData[i].physAddr = EndianSwap32( A_sglist_complete );
488: }
489:
490: srb->xferOffsetPrev = srb->xferOffset;
491: srb->xferOffset = offset;
492:
493: return ((rc != IO_R_SUCCESS) ? NO : YES) ;
494: }
495:
496: /*-----------------------------------------------------------------------------*
497: * Build SG list based on an IOMemoryDescriptor object.
498: *
499: *-----------------------------------------------------------------------------*/
500: - (BOOL) Sym8xxUpdateSGListDesc: (SRB *) srb
501: {
502:
503: PhysicalRange range;
504: u_int32_t actRanges;
505: u_int32_t offset;
506: u_int32_t bytesLeft;
507: u_int32_t i;
508: IOReturn rc = YES;
509:
510: offset = srb->xferOffset;
511: bytesLeft = srb->xferCount - srb->xferOffset;
512: i = 2;
513:
514: [(id)srb->xferBuffer setPosition: offset];
515:
516: while ( (bytesLeft > 0) && (i < MAX_SGLIST_ENTRIES-1))
517: {
518: [(id)srb->xferBuffer getPhysicalRanges: 1
519: maxByteCount: 0x00FFFFFF
520: newPosition: &offset
521: actualRanges: &actRanges
522: physicalRanges: &range];
523:
524: if ( actRanges != 1 )
525: {
526: rc = NO;
527: break;
528: }
529:
530: /*
531: * Note: The script instruction(s) to transfer data to/from the scsi bus
532: * have the same format as a typical SGList with the transfer length
533: * as the first word and the physical transfer address as the second.
534: * The data transfer direction is specified by a bit or'd into the
535: * high byte of the SG entry's length field.
536: */
537: srb->nexus.sgListData[i].physAddr = EndianSwap32( (u_int32_t)range.address );
538: srb->nexus.sgListData[i].length = EndianSwap32( range.length | srb->directionMask );
539:
540: bytesLeft -= range.length;
541: i++;
542: }
543:
544: if ( !bytesLeft )
545: {
546: srb->nexus.sgListData[i].length = EndianSwap32( 0x90080000 );
547: srb->nexus.sgListData[i].physAddr = EndianSwap32( 0x00000000 );
548: }
549: else
550: {
551: srb->nexus.sgListData[i].length = EndianSwap32( 0x98080000 );
552: srb->nexus.sgListData[i].physAddr = EndianSwap32( A_sglist_complete );
553: }
554:
555: srb->xferOffsetPrev = srb->xferOffset;
556: srb->xferOffset = offset;
557:
558: return rc;
559: }
560:
561:
562: /*-----------------------------------------------------------------------------*
563: * This routine allocates a SCSI Tag value for a request. For non-tagged requests
564: * a pseudo-tag is generated with the value target*16+lun.
565: *
566: * If all tags are in-use or a pseudo tag is in-use, the request is blocked until
567: * the tag becomes available.
568: *
569: *-----------------------------------------------------------------------------*/
570: - (u_int32_t) Sym8xxAllocTag:(SRB *) srb CmdQueue:(BOOL)fCmdQueue
571: {
572: u_int32_t i;
573: u_int32_t tagIndex;
574: u_int32_t tagMask;
575:
576: while ( 1 )
577: {
578: if ( fCmdQueue )
579: {
580: for ( i = MIN_SCSI_TAG; i < MAX_SCSI_TAG; i ++ )
581: {
582: tagIndex = i / 32;
583: tagMask = 1 << (i % 32);
584: if ( !(tags[tagIndex] & tagMask) )
585: {
586: tags[tagIndex] |= tagMask;
587: return i;
588: }
589: }
590: /*
591: * This semaphore gets unlocked whenever a tag gets returned to the pool. Any
592: * requests waiting for a tag will wake-up and try to allocate a tag. If they
593: * fail they will return here and will be put back to sleep.
594: */
595: [cmdQTagSem lock];
596: }
597: else
598: {
599: i = ((u_int32_t)srb->target << 3) | srb->lun;
600: tagIndex = i / 32;
601: tagMask = 1 << (i % 32);
602: if ( !(tags[tagIndex] & tagMask) )
603: {
604: tags[tagIndex] |= tagMask;
605: return i;
606: }
607: /*
608: * This per-target semaphore gets unlocked whenever a request completes on a target. Any
609: * requests pending for this target will wake-up and try to allocate this pseudo-tag. If they
610: * fail they will return here and will be put back to sleep.
611: */
612: [targets[srb->target].targetTagSem lock];
613: }
614: }
615: return -1;
616: }
617:
618: /*-----------------------------------------------------------------------------*
619: * This routine frees a previously allocates SCSI tag. It unlocks the appropriate
620: * semaphore based on the type of tag returned.
621: *
622: *-----------------------------------------------------------------------------*/
623: - (void) Sym8xxFreeTag:(SRB *) srb
624: {
625: u_int32_t i;
626:
627: i = srb->tag;
628: tags[i/32] &= ~(1 << (i % 32));
629:
630: if ( i < MIN_SCSI_TAG )
631: {
632: [targets[srb->target].targetTagSem unlock];
633: }
634: else
635: {
636: [cmdQTagSem unlock];
637: }
638: }
639:
640:
641: /*-----------------------------------------------------------------------------*
642: * This routine maintains a list of pages which are divided up into SRB sized
643: * allocations. The list of pages is grown or shrunk as needed.
644: *
645: * The reason we dont use the driverKit IOMalloc function is that it does not
646: * guarantee that allocations will not cross page boundaries. The driver does
647: * require this since the script accesses memory based on physical rather than
648: * virtual addresses.
649: *
650: *-----------------------------------------------------------------------------*/
651: - (SRB *) Sym8xxAllocSRB
652: {
653: SRBPool *pSRBPool;
654: SRB *pSRB = NULL;
655:
656: do
657: {
658: /*
659: * We hold the srbPoolLock when we are searching or changing the SRB pool
660: * data structures
661: */
662: [srbPoolLock lock];
663:
664: /*
665: * Search the list of pages currently in the SRB pool until we find a page
666: * with at least one free SRB to allocate.
667: */
668: pSRBPool = (SRBPool *) queue_first( &srbPool );
669: while (!queue_end( &srbPool, &pSRBPool->nextPage ) )
670: {
671: if ( !queue_empty( &pSRBPool->freeSRBList ) )
672: {
673: pSRBPool->srbInUseCount++;
674: queue_remove_first( &pSRBPool->freeSRBList, pSRB, SRB *, srbQ );
675: break;
676: }
677: pSRBPool = (SRBPool *)queue_next( &pSRBPool->nextPage );
678: }
679:
680: [srbPoolLock unlock];
681:
682: if ( pSRB )
683: {
684: bzero( pSRB, sizeof(SRB) );
685: pSRB->srbPhys = (SRB *)(pSRBPool->pagePhysAddr + (uint)pSRB - (uint)pSRBPool);
686: pSRB->srbSeqNum = ++srbSeqNum;
687: break;
688: }
689:
690: /*
691: * If we can find no available SRBs, we unlock a thread to grow the SRB pool and
692: * block this request until the pool grow operation completes. When our thread runs
693: * again it will retry the SRB allocation.
694: */
695: if ( srbPoolGrow == NO )
696: {
697: srbPoolGrow = YES;
698: [srbPoolGrowLock unlockWith: kSRBGrowPoolRunning];
699: }
700:
701: [srbPoolGrowLock lockWhen: kSRBGrowPoolIdle];
702: [srbPoolGrowLock unlockWith: kSRBGrowPoolIdle];
703: }
704: while ( 1 );
705:
706: return pSRB;
707: }
708:
709: /*-----------------------------------------------------------------------------*
710: * This routine returns SRBs to the SRB pool.
711: *
712: * The page in the pool containing the SRB is located and the
713: * SRB is added to that page's SRB free list.
714: *
715: * The pool is then scanned for pages with no SRBs allocated.
716: * If more than two pages are found with zero SRBs allocate, the
717: * additional idle pages are returned to the kernel.
718: *
719: *-----------------------------------------------------------------------------*/
720: - (void) Sym8xxFreeSRB: (SRB *) pSRB
721: {
722: SRB *srbMin, *srbMax;
723: SRBPool *pSRBPool, *pSRBPoolNext;
724: u_int32_t numSRBs;
725: kern_return_t kr;
726: u_int32_t idlePageCount = 0;
727:
728: [srbPoolLock lock];
729:
730: numSRBs = (page_size - sizeof(SRBPool)) / sizeof(SRB);
731:
732: /*
733: * Scan the pool for a page containing the returned SRB
734: */
735: pSRBPool = (SRBPool *) queue_first( &srbPool );
736: while (!queue_end( &srbPool, &pSRBPool->nextPage ) )
737: {
738: srbMin = (SRB *) (pSRBPool+1);
739: srbMax = &srbMin[numSRBs-1];
740:
741: if ( pSRB >= srbMin && pSRB <= srbMax )
742: {
743: pSRBPool->srbInUseCount--;
744: queue_enter( &pSRBPool->freeSRBList, pSRB, SRB *, srbQ );
745: break;
746: }
747: pSRBPool = (SRBPool *)queue_next( &pSRBPool->nextPage );
748: }
749:
750: /*
751: * If we fell off the end of the SRB Pool page list without finding
752: * the owning page, we have a bug.
753: */
754: if ( queue_end( &srbPool, &pSRBPool->nextPage ) )
755: {
756: kprintf("Sym8xxFreeSRB: Bad SRB returned = %08x\n\r", (u_int32_t)pSRB );
757: }
758:
759: /*
760: * We scan the SRBPool page list again looking for pages with no SRBs inuse.
761: * If more than idle pool pages are found, we release the remaining pages to
762: * the kernel.
763: */
764: pSRBPool = (SRBPool *) queue_first( &srbPool );
765: while (!queue_end( &srbPool, &pSRBPool->nextPage ) )
766: {
767: pSRBPoolNext = (SRBPool *)queue_next( &pSRBPool->nextPage );
768:
769: if ( !pSRBPool->srbInUseCount )
770: {
771: if ( ++idlePageCount > kSRBPoolMaxFreePages )
772: {
773: queue_remove( &srbPool, pSRBPool, SRBPool *, nextPage );
774:
775: // kprintf("SCSI(Symbios8xx): Sym8xxShrinkSRBPool\n\r");
776:
777: kr = kmem_free(IOVmTaskSelf(), (vm_offset_t) pSRBPool, page_size );
778: if ( kr != KERN_SUCCESS )
779: {
780: IOPanic("SCSI(Symbios8xx): kmem_free failed - Help me\n\r");
781: }
782: }
783: }
784: pSRBPool = pSRBPoolNext;
785: }
786:
787: [srbPoolLock unlock];
788:
789: }
790:
791: /*-----------------------------------------------------------------------------*
792: * This routines grows the SRBPool. It runs on its own thread to avoid pager deadlocks.
793: *
794: * We need this entry thunk since the thread creation routines dont support objC
795: * interfaces directly.
796: *
797: *-----------------------------------------------------------------------------*/
798: IOThreadFunc Sym8xxGrowSRBPool( Sym8xxController *controller )
799: {
800: [controller Sym8xxGrowSRBPool];
801: return NULL;
802: }
803:
804: - (void) Sym8xxGrowSRBPool
805: {
806: SRBPool *pSRBPool;
807: SRB *pSRB;
808: kern_return_t kr;
809: u_int32_t numSRBs;
810: u_int32_t i;
811:
812: while ( 1 )
813: {
814: [srbPoolGrowLock lockWhen: kSRBGrowPoolRunning];
815:
816: // kprintf("SCSI(Symbios8xx): Sym8xxGrowSRBPool\n\r");
817:
818: kr = kmem_alloc_wired(IOVmTaskSelf(), (vm_offset_t *) &pSRBPool, page_size );
819: if ( kr != KERN_SUCCESS )
820: {
821: IOPanic("kmem_alloc_wired failed - Help me\n\r");
822: }
823:
824: IOPhysicalFromVirtual((vm_task_t)IOVmTaskSelf(), (vm_offset_t)pSRBPool, (vm_offset_t *)&pSRBPool->pagePhysAddr );
825:
826: pSRBPool->srbInUseCount = 0;
827:
828: numSRBs = (page_size - sizeof(SRBPool)) / sizeof(SRB);
829: pSRB = (SRB *) (pSRBPool+1);
830:
831: queue_init( &pSRBPool->freeSRBList );
832: for ( i=0; i < numSRBs; i++ )
833: {
834: queue_enter( &pSRBPool->freeSRBList, (pSRB+i), SRB *, srbQ );
835: }
836:
837: [srbPoolLock lock];
838: queue_enter( &srbPool, pSRBPool, SRBPool *, nextPage );
839: [srbPoolLock unlock];
840:
841: srbPoolGrow = NO;
842: [srbPoolGrowLock unlockWith: kSRBGrowPoolIdle];
843: }
844: }
845:
846:
847: /*-----------------------------------------------------------------------------*
848: * This routine interfaces between the system timer and our I/O Thread. It
849: * sends a message to the IOThread to run the -timeoutOccurred routine which
850: * does various timing functions for the driver. See Sym8xxExecuteRequest(timeoutOccurred).
851: *
852: *-----------------------------------------------------------------------------*/
853: IOThreadFunc Sym8xxTimerReq( Sym8xxController *device )
854: {
855: msg_header_t msg = { 0 };
856:
857: msg.msg_size = sizeof (msg);
858: msg.msg_remote_port = device->interruptPortKern;
859: msg.msg_id = IO_TIMEOUT_MSG;
860:
861: msg_send_from_kernel(&msg, MSG_OPTION_NONE, 0);
862:
863: return NULL;
864: }
865:
866: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.