File:  [Apple Darwin 0.x] / driverkit / libDriver / Kernel / IOMemoryDescriptor.m
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:37:51 2018 UTC (8 years, 3 months ago) by root
Branches: MAIN, Apple
CVS tags: HEAD, Darwin03, Darwin02
Darwin 0.2 Driver Kit

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.1 (the "License").  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/**
 * IOMemoryDescriptor.m
 * Copyright 1997-98 Apple Computer Inc. All Rights Reserved.
 *
 * IOMemoryDescriptor describes the client memory needed to perform an I/O
 * request. It contains a single IOMemoryContainer that describes one or
 * more ranges of memory.  IOMemoryDescriptor is used directly for simple
 * (non-RAID) transfers, and is the underlying object used for striped or
 * mirrored requests. Logical and Physical memory cannot be mixed in a single
 * IOMemoryDescriptor. Several IOMemoryDescriptors may reference a single
 * IOMemoryContainer (by using the replicate method).
 */
#import <driverkit/IOMemoryDescriptor.h>
#import <mach/vm_param.h>
#import <limits.h>

/*
 * Compute the start of the next physical page
 */
#define next_page(x)	(trunc_page(((unsigned int) x) + page_size))

@interface IOMemoryDescriptor(Private)
/**
 * Initialize an IOMemoryDescriptor object.
 */
- (id)			initWithIOMemoryContainer
				: (IOMemoryContainer *) ioMemoryContainer;
/**
 * Free the descriptor and it's container.
 */
- (id)			free;
/*
 * validate is called when the state needs to be validated.
 */
- (void)		validate;
/*
 * Retrieve the current logical range (returns a zero-length range on failure).
 */
- (void)		getCurrentLogicalRange;

/*
 * Retrieve the current physical range. This can fail if the O.S. returns
 * an error status.
 */
- (IOReturn)		getCurrentPhysicalRange;

/*
 * Force the state to the end of the entire container (for reposition
 * errors).
 */
- (void)		setPositionAtEnd;
@end /* IOMemoryContainer(Private) */

@implementation IOMemoryDescriptor(Private)
/**
 * Create an empty IOMemoryDescriptor object.
 */
- (id)			initWithIOMemoryContainer
				: (IOMemoryContainer *) thisIOMemoryContainer
{
	ioMemoryContainer	= thisIOMemoryContainer;
	state.currentOffset	= 0;
	retainCount		= 1;
	valid			= FALSE;	/* Resets other values	    */
	[self setMaxSegmentCount : UINT_MAX];
	return (self);
}

/**
 * Dispose of the object. This decrements the IOMemoryContainer's
 * reference count and frees the IOMemoryContainer if this IOMemoryDescriptor
 * is the last referencer.
 */
- 				free
{
	[ioMemoryContainer release];
	return ([super free]);
}

/**
 * Extract the next logical range from the IOMemoryContainer.
 * If this fails because rangeIndex exceeds the container max,
 * a zero-length range will be returned.
 */
- (void)		getCurrentLogicalRange
{
    if ([ioMemoryContainer logicalRange: &state.ioRange
                                  index: state.rangeIndex] != IO_R_SUCCESS) {
	    state.ioRange.start		= 0xDEADBEEF;
	    state.ioRange.size		= 0xFFFFFFFF;
	}
	state.logicalOffset		= 0;
	state.physicalOffset		= 0;
}

/**
 * Return the physical address that corresponds to the current logical
 * address. The state is valid at this point. This method sets the
 * physicalPageLength to the maximumn number of bytes that can be
 * transferred in this page.
 */
- (IOReturn)		getCurrentPhysicalRange
{
   	IOReturn		ioReturn;
   	unsigned int		nextPageAddress;
   	
 	state.physicalOffset = 0;	/* At page start	*/
   	ioReturn	= IOPhysicalFromVirtual(
   				[ioMemoryContainer client],
   				state.ioRange.start + state.logicalOffset,
   				(vm_offset_t *) &state.physical.address
   			);
   	if (ioReturn != IO_R_SUCCESS) {
       	     state.physical.address	= 0;
   	     state.physical.length	= 0;
   	}
	else {
	    /*
	     * The amount we can transfer is the number of bytes
	     * from the current start index to the next page
	     * (limited by scsiReq->maxTransfer). Mach lacks
	     * a method for determining the page size. We'll
	     * use 4096 for convenience (the only cost is extra
	     * cycles through the for loop).
	     */
	     nextPageAddress = next_page(state.physical.address);
	     state.physical.length =
			nextPageAddress - ((unsigned int) state.physical.address);
	}
	return (ioReturn);
}

/**
 * Validate the state so that all state values agree with
 * the currentOffset.
 */
- (void)		validate
{
	unsigned int	totalByteCount = [ioMemoryContainer totalByteCount];
	unsigned int	totalRangeCount = [ioMemoryContainer rangeCount];

	if (state.currentOffset >= totalByteCount) {
	    [self setPositionAtEnd];
	}
	else {
	    unsigned int	offsetAtRangeStart = 0;
	    unsigned int	offsetAtRangeEnd;
	    for (state.rangeIndex = 0;
			state.rangeIndex < totalRangeCount;
			state.rangeIndex++) {

                /* Check for an invalid range or other problems */
		[self getCurrentLogicalRange];
		if (0xDEADBEEF == state.ioRange.start
		&&  0xFFFFFFFF == state.ioRange.size) {
		    IOPanic("IOMemoryDescriptor: "
                            "validate fell of the end of the world\n");
		}
		offsetAtRangeEnd = offsetAtRangeStart + state.ioRange.size;
		if (offsetAtRangeEnd > state.currentOffset) {
		    state.logicalOffset =
			state.currentOffset - offsetAtRangeStart;
		    break;			/* Normal exit		*/
		}
		offsetAtRangeStart = offsetAtRangeEnd;
	    }
	    if (state.rangeIndex >= totalRangeCount) {
		[self setPositionAtEnd];	/* Bug: can't happen	*/
	    }
	}
	state.physicalOffset = 0;		/* Invalidate physical	*/
	valid = TRUE;
}

/*
 * Force the state to the end of the entire container (for reposition
 * errors).
 */
- (void)		setPositionAtEnd
{
	state.rangeIndex = [ioMemoryContainer rangeCount] - 1;
    	[self getCurrentLogicalRange];
    	state.logicalOffset = state.ioRange.size;
}

@end /* IOMemoryDescriptor(Private) */

@implementation IOMemoryDescriptor

/**
 * Initialize an IOMemoryDescriptor object for a logical scatter-gather list.
 * The scatter-gather list is provided in DriverKit IORange format. If byReference
 * is TRUE, the associated IOMemoryContainer will hold a reference to the range
 * vector (which must remain addressable during the lifetime of the object). If
 * FALSE, the range vector will be copied into the IOMemoryContainer object.
 */
- (id)			initWithIORange
			: (const IORange *) ioRange
	count		: (unsigned int) count
	byReference	: (BOOL) byReference
{
	IOMemoryContainer	*thisIOMemoryContainer =
		[[IOMemoryContainer alloc] initWithIORange
					: ioRange
			count		: count
			byReference	: byReference
		    ];
	[self initWithIOMemoryContainer	: thisIOMemoryContainer];
	return (self);

}

/**
 * Initialize an IOMemoryDescriptor object for a single logical range.
 */
- (id)			initWithAddress
			: (void *) address
	length		: (unsigned int) length
{
	IOMemoryContainer	*thisIOMemoryContainer =
		[[IOMemoryContainer alloc] initWithAddress
					: address
			length		: length
		    ];
	[self initWithIOMemoryContainer	: thisIOMemoryContainer];
	return (self);
}


- (id)			initWithIOV
			: (const struct iovec *) iov
	count		: (unsigned int) count
{
	IOMemoryContainer	*thisIOMemoryContainer =
		[[IOMemoryContainer alloc] initWithIOV
					: iov
			count		: count
		    ];
	[self initWithIOMemoryContainer	: thisIOMemoryContainer];
	return (self);
}

/**
 * Manage the retain/release reference count. See NSObject for details.
 */
- (unsigned int)	retainCount
{
	return (retainCount);
}

- (id)			retain
{
	++retainCount;
	return (self);
}

- (oneway void)		release
{
	if (--retainCount == 0) {
	    [self free];
	}
}


/**
 * Return a copy of this IOMemoryDescriptor and its IOMemoryContainer.
 * The IOMemoryContainer's reference count will be incremented.
 * The current position is not duplicated: the clone will be reset
 * to position zero.
 */
- (id)			replicate
{
	IOMemoryDescriptor	*result;

	[ioMemoryContainer retain];
	result = [[self copy] initWithIOMemoryContainer
					: ioMemoryContainer
		];
	if (result) {
	    [result setMaxSegmentCount	: maxSegmentCount];
	    [result setClient		: [self client]];
	}
	return (result);
}

/**
 * Accessor methods
 */
- (unsigned int)	currentOffset
{
	return (state.currentOffset);
}

- (unsigned int)	totalByteCount
{
	return ([ioMemoryContainer totalByteCount]);
}

- (unsigned int)	maxSegmentCount
{
	return (maxSegmentCount);
}
/*
 * Retrieve the ioMemoryContainer
 */
- (id)			ioMemoryContainer
{
	return (ioMemoryContainer);
}



- (void)		setMaxSegmentCount
			: (unsigned int) newMaxSegmentCount
{
        maxSegmentCount	= (newMaxSegmentCount == 0)
			? [self totalByteCount]
			: newMaxSegmentCount;
}

- (vm_task_t)		client
{
	return ([ioMemoryContainer client]);
}

- (void)		setClient
			: (vm_task_t) client
{
	[ioMemoryContainer setClient : client];
}

- (void)		setIOMemoryContainer
			: (id) newIOMemoryContainer
{
	if (ioMemoryContainer != newIOMemoryContainer) {
	    [ioMemoryContainer release];
	    [newIOMemoryContainer retain]; 
	    ioMemoryContainer	= newIOMemoryContainer;
	}
	state.currentOffset	= 0;
	valid			= FALSE;	/* Resets other values	    */
}

/**
 * This retrieves all positioning information -- it is intended for
 * processing SCSI Save Data Pointers messages. Callers should treat
 * IOMemoryDescriptorState as an opaque object.
 */
- (void)		state
			: (IOMemoryDescriptorState *) statePtr
{
	/*
	 * If we're called without a valid state, setState will
	 * fail (as the new state will be invalid). Note that the
	 * state is invalid when the IOMemoryDescriptor is first
	 * created.
	 */
	if (valid == FALSE) {
	    [self validate];
	}
	*statePtr = state;
}


/**
 * Reposition the IOMemoryDescriptor's current access point. (The TECO "dot").
 * setState should be be called with the results of a previous state
 * method. It is very fast. The caller must not modify the state.
 */
- (void)		setState
			: (const IOMemoryDescriptorState *) statePtr
{
	state		= *statePtr;
	valid		= TRUE;
}

/**
 * Set the current access point to the specified byte index. This is an
 * absolute position within the IOMemoryDescriptor. This will be slow
 * for complex memory descriptors. setPosition does not check the parameter
 * for validity -- this is done by the nextPhysicalRange method.
 */
- (void)		setPosition
			: (unsigned int) newPosition
{
	if (state.currentOffset != newPosition) {
		state.currentOffset	= newPosition;
		valid			= FALSE;
	}
}

/**
 * Set the current access point to the relative position with respect
 * to the current position. This is equivalent to writing:
 *    [ioMemoryDescriptor setPosition
 *		: [ioMemoryDescriptor currentOffset] + offset];
 * This will be slow for complex memory descriptors. setOffset does not check
 * the parameter for validity -- this is done by the nextPhysicalRange method.
 */
- (void)		setOffset
			: (signed int) offset
{
	state.currentOffset	+= offset;
	valid			= FALSE;
}


/**
 * Return one or more logical ranges. Return zero if the transfer is outside
 * of the defined range (I.e., if all data has been transferred).
 * @param maxRanges		The maximum number of ranges to retrieve.
 * @param maxByteCount		The maximum number of bytes to retrieve
 *				in the entire sequence. To use the remaining
 *				transfer count, specify UINT_MAX (from limits.h).
 * @param newPosition		The new value of currentOffset (may be NULL)
 * @param actualRanges		The actual number of ranges retrieved
 *				(NULL if not needed)
 * @param logicalRanges		A vector of logical range elements.
 * Return the total number of bytes in all ranges. Return zero if the current
 * offset is beyond the end of the range.
 */
- (unsigned int)	getLogicalRanges
			: (unsigned int) maxRanges
	maxByteCount	: (unsigned int) maxByteCount
	newPosition	: (unsigned int *) newPosition
	actualRanges	: (unsigned int *) actualRanges
	logicalRanges	: (IORange *) logicalRanges
{
	unsigned int	byteCount;
	unsigned int	transferCount	= 0;
	unsigned int	rangeCount;
	unsigned int	totalByteCount	= [ioMemoryContainer totalByteCount];

	if (valid == FALSE) {
	    [self validate];
	}
	for (rangeCount = 0; rangeCount < maxRanges; rangeCount++) {
	    byteCount = totalByteCount - state.currentOffset;
	    if (state.logicalOffset >= state.ioRange.size) {
		++state.rangeIndex;
		[self getCurrentLogicalRange];
	    }
	    if (byteCount > (state.ioRange.size - state.logicalOffset)) {
		byteCount = state.ioRange.size - state.logicalOffset;
	    }
	    if (byteCount > maxSegmentCount) {
		byteCount = maxSegmentCount;
	    }
	    if (byteCount > maxByteCount) {
	    	byteCount = maxByteCount;
	    }
	    if (byteCount == 0) {
		break;		/* Fell off the end	*/
	    }
	    logicalRanges->size		= byteCount;
	    logicalRanges->start	= state.ioRange.start
					+ state.logicalOffset;
	    ++logicalRanges;
	    state.logicalOffset		+= byteCount;
	    transferCount		+= byteCount;
	    state.currentOffset += transferCount;
	}
	if (actualRanges != NULL) {
	    *actualRanges		= rangeCount;
	}
	if (newPosition != NULL) {
	    *newPosition		= state.currentOffset;
	}
	state.physicalOffset		= 0;	/* Invalid physical range    */
	return (transferCount);
}

/**
 * Return one or more physical ranges. Return zero if the transfer is outside
 * of the defined range (I.e., if all data has been transferred).
 * @param maxRanges		The maximum number of ranges to retrieve.
 * @param maxByteCount		The maximum number of bytes to retrieve
 *				in the entire sequence. To use the remaining
 *				transfer count, use UINT_MAX (from limits.h).
 * @param newPosition		The new value of currentOffset (may be NULL)
 * @param actualRanges		The actual number of ranges retrieved
 *				(NULL if not needed)
 * @param physicalRanges	A vector of physical range elements.
 * Return the total number of bytes in all ranges. Return zero if the current
 * offset is beyond the end of the range.
 */
- (unsigned int)			getPhysicalRanges
			: (unsigned int) maxRanges
	maxByteCount	: (unsigned int) maxByteCount
	newPosition	: (unsigned int *) newPosition
	actualRanges	: (unsigned int *) actualRanges
	physicalRanges	: (PhysicalRange *) physicalRanges
{
	unsigned int	byteCount;
	unsigned int	transferCount	= 0;
	unsigned int	rangeCount;
	unsigned int	totalByteCount	= [ioMemoryContainer totalByteCount];

	if (valid == FALSE) {
	    [self validate];
	}
	for (rangeCount = 0; rangeCount < maxRanges; rangeCount++) {
	    byteCount = totalByteCount - state.currentOffset;
	    if (state.logicalOffset >= state.ioRange.size) {
		++state.rangeIndex;
		[self getCurrentLogicalRange];
	    }
	    if (byteCount > (state.ioRange.size - state.logicalOffset)) {
		byteCount = state.ioRange.size - state.logicalOffset;
	    }
	    if ((byteCount + transferCount) > maxSegmentCount) {
		byteCount = maxSegmentCount - transferCount;
	    }
	    if ((byteCount + transferCount) > maxByteCount) {
	    	byteCount = maxByteCount - transferCount;
	    }
	    if (byteCount == 0) {
		break;		/* Fell off the end	*/
	    }
	    if (state.physicalOffset == 0
	     || state.physicalOffset >= state.physical.length) {
		if ([self getCurrentPhysicalRange] != IO_R_SUCCESS) {
		    break;
		}
	    }
	    if (byteCount > (state.physical.length - state.physicalOffset)) {
		byteCount = state.physical.length - state.physicalOffset;
	    }
	    physicalRanges->length	= byteCount;
	    physicalRanges->address	= (void *)
		(((unsigned int) state.physical.address)
			+ state.physicalOffset);
	    ++physicalRanges;
	    transferCount		+= byteCount;
	    state.physicalOffset	+= byteCount;
	    state.logicalOffset		+= byteCount;
	    state.currentOffset		+= byteCount;
	}
	if (actualRanges != NULL) {
	    *actualRanges		= rangeCount;
	}
	if (newPosition != NULL) {
	    *newPosition		= state.currentOffset;
	}
	return (transferCount);
}

/**
 * Copy bytes from the caller's address space to the IOMemoryContainer
 * client address space. This is an inefficient routine that should only
 * be used for "corner cases" such as handling unaligned transfers.
 * The copy begins at the current logical address. The current logical
 * position will be incremented.
 * @param buffer	The starting buffer address in the caller's
 *			address space.
 * @param count		The number of bytes to transfer.
 * @return		The actual number of bytes transferred.
 */
- (unsigned int)		writeToClient
			: (void *) buffer
		count	: (unsigned int) count
{
	unsigned int	totalByteCount;
	unsigned int	thisCount;
	PhysicalRange	range;
	vm_address_t	clientVirtualAddress;
	
	for (totalByteCount = 0; totalByteCount < count;) {
	    thisCount = [self getPhysicalRanges
					: 1	/* Only one range	*/
		    maxByteCount	: count - totalByteCount
		    newPosition		: NULL
		    actualRanges	: NULL
		    physicalRanges	: &range
		];
	    if (thisCount == 0) {
		break;	/* Ran off the end of the user buffer	*/
	    }
	    clientVirtualAddress = [self mapPhysicalAddressIntoIOTask : &range];
	    if (clientVirtualAddress == (vm_address_t) NULL) {
		[self setOffset : -range.length];
		break;
	    }
	    else {
		bcopy(buffer, clientVirtualAddress, range.length);
		((unsigned char *) buffer) += range.length;
		totalByteCount += range.length;
		[self unmapVirtualAddressFromIOTask
					: clientVirtualAddress
			length		: range.length
		    ];
	    }
	}
	return (totalByteCount);
}

/**
 * Copy bytes from the IOMemoryContainer client's address space to the
 * caller's address space. This is an inefficient routine that should only
 * be used for "corner cases" such as handling unaligned transfers.
 * The copy begins at the current logical address. The current logical
 * position will be incremented.
 * @param buffer	The starting buffer address in the caller's
 *			address space.
 * @param count		The number of bytes to transfer.
 * @return		The actual number of bytes transferred.
 */
- (unsigned int)		readFromClient
			: (void *) buffer
		count	: (unsigned int) count
{
	unsigned int	totalByteCount;
	unsigned int	thisCount;
	PhysicalRange	range;
	vm_address_t	clientVirtualAddress;
	
	for (totalByteCount = 0; totalByteCount < count;) {
	    thisCount = [self getPhysicalRanges
					: 1	/* Only one range	*/
		    maxByteCount	: count - totalByteCount
		    newPosition		: NULL
		    actualRanges	: NULL
		    physicalRanges	: &range
		];
	    if (thisCount == 0) {
		break;	/* Ran off the end of the user buffer	*/
	    }
	    clientVirtualAddress = [self mapPhysicalAddressIntoIOTask : &range];
	    if (clientVirtualAddress == (vm_address_t) NULL) {
		[self setOffset : -range.length];
		break;
	    }
	    else {
		bcopy(clientVirtualAddress, buffer, range.length);
		((unsigned char *) buffer) += range.length;
		totalByteCount += range.length;
		[self unmapVirtualAddressFromIOTask
					: clientVirtualAddress
			length		: range.length
		    ];
	    }
	}
	return (totalByteCount);
}

/**
 * mapPhysicalAddressIntoIOTask is used to map a potentially
 * unaligned physical address and range into our virtual address
 * space. It is a direct wrapper for IOMapPhysicalIntoIOTask
 * that handles unaligned physical addresses.
 */ 
- (vm_address_t) mapPhysicalAddressIntoIOTask
		: (const PhysicalRange *) range
{
	vm_address_t	clientVirtualAddress;
	unsigned int		offset;
	IOReturn	ioReturn;

	offset = ((unsigned int) range->address) & page_mask;
	ioReturn = IOMapPhysicalIntoIOTask(
		range->address - offset,
		range->length + offset,
		&clientVirtualAddress
	    );
	if (ioReturn != IO_R_SUCCESS) {
	    clientVirtualAddress = (vm_address_t) NULL;
	}
	else {
	    /*
	     * Ensure that clientVirtualAddress is on a page
	     * boundary and add in the actual offset.
	     */
	    clientVirtualAddress = (vm_address_t)
			(((unsigned int) clientVirtualAddress) & ~page_mask)
			+ offset;
	}
	return (clientVirtualAddress);
}
		
/**
 * Un-map a range that was mapped by mapPhysicalAddressIntoIOTask.
 * rangeLength must be identical to the range.length parameter passed
 * to mapPhysicalAddressIntoIOTask.
 */
- (void) unmapVirtualAddressFromIOTask
		: (vm_address_t) clientVirtualAddress
	length	: (unsigned int) rangeLength
{
	unsigned int		offset;
	vm_address_t	pageBaseAddress;

	offset = ((unsigned int) clientVirtualAddress) & page_mask;
	pageBaseAddress = (vm_address_t)
		((unsigned int) clientVirtualAddress) & ~page_mask;
	(void) IOUnmapPhysicalFromIOTask(
		pageBaseAddress, rangeLength + offset);
}

/**
 * Make the memory described by this IOMemoryDescriptor resident.
 * This is called by the virtual memory manager and/or file system before
 * starting an I/O request. Residency is an all-or-nothing process. The
 * IOMemoryDescriptor maintains a reference count: the first caller makes the
 * memory resident; the others just increment the count. This method returns
 * an error status if any range cannot be made resident and all memory will
 * be made pageable. This method may only be called by kernel servers.
 */
- (IOReturn)			wireMemory
			: (BOOL) forReading
{
	return ([ioMemoryContainer wireMemory : forReading]);
}

/**
 * Make the memory described by this underlying IOMemoryDescriptor pageable.
 * This is called by the virtual memory manager and/or file system after
 * completing an I/O request.  The IOMemoryDescriptor maintains a reference
 * count: the last caller frees the memory the others just decrement the count.
 * This method returns an error status if any range could not be freed,
 * but always tries to free all ranges. Return IO_R_VM_FAILURE if any range
 * can't be unwired (but there is no indication as to which range).
 */
- (IOReturn)			unwireMemory
{
	return ([ioMemoryContainer unwireMemory]);
}

/**
* Normalize cache coherency (if needed by this particular hardware
* architecture) before starting a DMA operation. This normalizes all
* memory described by this IOMemoryContainer.  This is used as follows:
*	mem = [IOMemoryDescriptor allocLogicalRange
*				: address
*			length	: length];
*	[mem makeResident];
*	[mem checkpoint : ioCheckpointInput];
*	... Extract physical ranges and do DMA I/O ...
*	[mem checkpoint : ioCheckpointComplete];
*	[mem makePageable];
*	[mem free];
* A checkpoint call may specify any combination of ioCheckpointInput,
* ioCheckpointOutput, or ioCheckpointNoDirection. After DMA completes,
* drivers must call checkpoint with ioCheckpointComplete as the only
* parameter. The actual operation of checkpoint is processor-specific.
 */
- (IOReturn)			checkpoint
			: (IOMemoryCheckpointOption) option
{
/*
 * *** Hmm: if we have multiple users, the checkpoint probably should be
 * *** inside the descriptor, not the container
 */
	return ([ioMemoryContainer checkpoint : option]);
}

@end /* IOMemoryDescriptor : IOObject */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.