File:  [Apple Darwin 0.x] / driverkit / libDriver / eisa / IOEISADirectDevice.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, 2 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@
 */
/*
 * Copyright (c) 1993 NeXT Computer, Inc.
 *
 * ISA/EISA direct device implementation.
 *
 * HISTORY
 *
 * 10Jan93 Brian Pinkerton at NeXT
 *	Created.
 *
 *
 *  TODO:
 *	Implement IOPort reservation
 *	Verify that host-master DMA is working with DavidS
 */

#define KERNEL_PRIVATE	1

#import <objc/List.h>
#import <driverkit/i386/directDevice.h>
#import <driverkit/IODirectDevicePrivate.h>
#import <driverkit/i386/IOEISADeviceDescription.h>
#import <driverkit/driverTypes.h>
#import <driverkit/i386/driverTypesPrivate.h>
#import <driverkit/generalFuncs.h>
#import <driverkit/i386/driverTypes.h>
#import <driverkit/i386/EISAKernBus.h>
#import <driverkit/KernDevice.h>
#import <driverkit/KernDeviceDescription.h>
#import <driverkit/kernelDriver.h>
#import <driverkit/interruptMsg.h>
#import <machdep/i386/dma_exported.h>
#import <machdep/i386/dma.h>
#import <machdep/i386/xpr.h>
#import <machkit/NXLock.h>
#import <kernserv/lock.h>
#import <kernserv/prototypes.h>

#import <objc/HashTable.h>

extern NXLock 	*dmaLock;	// in autoconf_i386.m

@interface IODirectDevice(EISAPrivate)

- (unsigned int) _localToChannel:(unsigned int) localChannel;
- initEISA;
- freeEISA;

@end

struct _eisa_private {
    Arch	type;
};

@implementation IODirectDevice(IOEISADirectDevice)

- initEISA
{
    	struct _eisa_private	*private;

	private = _busPrivate = (void *)IOMalloc(sizeof (*private));
	private->type = EISA;
	
	return self;
}

- freeEISA
{
    	struct _eisa_private	*private = _busPrivate;
	
	IOFree((void *)private, sizeof (*private));
	
	return self;
}

/*
 *  A bunch of convenient private API:
 *
 *  Map from local channel numbers to real ones, and from local IRQ numbers
 *  to real ones.
 */
- (unsigned int) _localToChannel:(unsigned int) localChannel
{
	return [(IOEISADeviceDescription *)_deviceDescription channelList][localChannel];
}


/*
 *  Routines for dealing with interrupts start here.
 */

- (IOReturn) reserveInterrupt	: (unsigned int)localInterrupt
{
	return IO_R_SUCCESS;
}

- (void) releaseInterrupt	: (unsigned int) localInterrupt
{
}




/*
 *  The subclass overrides this method.  In the superclass, we just
 *  return NO.
 */
- (BOOL) getHandler:(IOEISAInterruptHandler *)handler
              level:(unsigned int *)ipl
	   argument:(unsigned int *) arg
       forInterrupt:(unsigned int) localInterrupt
{
	return NO;
}


- (IOReturn) reserveChannel	: (unsigned int) localChannel
{
	return IO_R_SUCCESS;
}

- (void) releaseChannel		: (unsigned int) localChannel
{
}

- (IOReturn) enableChannel:(unsigned int) localChannel
{
	unsigned int realChannel;
	
	if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
		return IO_R_INVALID_ARG;

	realChannel = [self _localToChannel:localChannel];

	dma_unmask_chan(realChannel);
	return IO_R_SUCCESS;
}


- (void) disableChannel:(unsigned int) localChannel
{
	unsigned int realChannel;
	
	if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
		return;

	realChannel = [self _localToChannel:localChannel];

	dma_mask_chan(realChannel);
}


- (IOReturn) setTransferMode	: (IODMATransferMode) mode 
		     forChannel : (unsigned int) localChannel;
{
	int real_mode;
	unsigned int realChannel;
	
	if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
		return IO_R_INVALID_ARG;

	realChannel = [self _localToChannel:localChannel];

	switch(mode) {
	    case IO_Demand:
	    	real_mode = DMA_MODE_DEMAND;
		break;
	    case IO_Single:
	    	real_mode = DMA_MODE_SINGLE;
		break;
	    case IO_Block:
	    	real_mode = DMA_MODE_BLOCK;
		break;
	    case IO_Cascade:
	    	real_mode = DMA_MODE_CASCADE;
		break;
	}
	dma_chan_xfer_mode(realChannel, real_mode);
	return IO_R_SUCCESS;
}

/*
 * Enable/disable autoinitialize DMA mode. Default is 
 * disabled.
 */
- (IOReturn)setAutoinitialize  	: (BOOL)flag
                    forChannel 	: (unsigned)localChannel
{
	unsigned int realChannel;
	
	if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
		return IO_R_INVALID_ARG;

	realChannel = [self _localToChannel:localChannel];

	dma_chan_autoinit(realChannel, (flag ? 1 : 0));
	return IO_R_SUCCESS;
}

/*
 * Set DMA address increment/decrement mode.
 * Default is IO_Increment.
 */
- (IOReturn)setIncrementMode   	: (IOIncrementMode)mode
                    forChannel 	: (unsigned)localChannel
{
	unsigned int realChannel;
	int dir;
	
	if(mode == IO_Decrement) {
		return IO_R_UNSUPPORTED;		// nope!
	}
	if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
		return IO_R_INVALID_ARG;

	realChannel = [self _localToChannel:localChannel];

	dir = (mode == IO_Decrement ? DMA_ADRS_DECR : DMA_ADRS_INCR);
	dma_chan_adrs_dir(realChannel, dir);
	return IO_R_SUCCESS;
}


/*
 *  Returns a DMA buffer for the contents of physical memory starting at
 *  addr and continuing for length bytes.  If the physical address changed
 *  to accommodate the ISA bus, the new physical address is returned in
 *  place.  The DMABuffer is an opaque type.
 *
 *  Warning: this method allocates memory, so it can block!
 */
- (IOEISADMABuffer) createDMABufferFor:(unsigned int *) physAddr
			 length:(unsigned int) length
			 read:(BOOL) isRead
			 needsLowMemory:(BOOL) lowerMem
			 limitSize:(BOOL) limitSize
{
	dma_xfer_t *xfer = IOMalloc(sizeof(dma_xfer_t));

	if (xfer == NULL)
		return NULL;

	xfer->phys = *physAddr;
	xfer->len = length;
	xfer->read = isRead;
	xfer->lower16 = lowerMem;
	xfer->bound64 = limitSize;
	xfer->buffered = FALSE;

	if (!dma_xfer(xfer, physAddr)) {
		xpr_dmabuf("createDMABufferFor: dma_xfer() failed; physAddr"
			"0x%x\n", physAddr, 2,3,4,5);
		IOFree(xfer, sizeof(dma_xfer_t));
		return NULL;
	}
	
	xpr_dmabuf("createDMABufferFor: xfer 0x%x\n", xfer, 2,3,4,5);
	return (IOEISADMABuffer) xfer;
}


- (void) freeDMABuffer:(IOEISADMABuffer) dmaBuffer
{
	dma_xfer_t *xfer = (dma_xfer_t *) dmaBuffer;
	
	xpr_dmabuf("freeDMABuffer: dmaBuffer active %d 0x%x\n", 
		xfer, xfer->active,3,4,5);
	if (xfer->active)
		dma_xfer_done(xfer);
	IOFree(xfer, sizeof(dma_xfer_t));
}


- (void) abortDMABuffer:(IOEISADMABuffer) dmaBuffer
{
	dma_xfer_t *xfer = (dma_xfer_t *) dmaBuffer;
	
	xpr_dmabuf("abortDMABuffer: dmaBuffer active %d 0x%x\n", 
		xfer, xfer->active,3,4,5);
	if (xfer->active)
		dma_xfer_abort(xfer);
	IOFree(xfer, sizeof(dma_xfer_t));
}

- (IOReturn) startDMAForBuffer:(IOEISADMABuffer) buffer channel:(unsigned int) localChannel
{
	unsigned int realChannel;
	
	if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
		return IO_R_INVALID_ARG;

	realChannel = [self _localToChannel:localChannel];

	if (dma_xfer_chan(realChannel, (dma_xfer_t *) buffer))
		return IO_R_SUCCESS;
	else
		return IO_R_NO_FRAMES;			/* XXX */
}

/*
 * Return localChannel's current address and count.
 */
- (unsigned)currentAddressForChannel	: (unsigned)localChannel
{
	unsigned int realChannel;
	
	if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
		return 0;
	realChannel = [self _localToChannel:localChannel];
	return(get_dma_addr(realChannel));
}

- (unsigned)currentCountForChannel	: (unsigned)localChannel
{
	unsigned int realChannel;
	
	if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
		return 0;
	realChannel = [self _localToChannel:localChannel];
	return(get_dma_count(realChannel));
}


- (BOOL) isDMADone:(unsigned int) localChannel
{
	if (localChannel >= [(IOEISADeviceDescription *)_deviceDescription numChannels])
		return NO;

	return (is_dma_done([self _localToChannel:localChannel]) ?
		YES : NO);
}




/*
 * Determine whether or not the aassociated device is connected to an EISA 
 * bus. Returns YES if so, else returns NO.
 */
extern BOOL is_ISA;		// in driverkit/i386/autoconf_i386.m

- (BOOL)isEISAPresent
{
	return (is_ISA ? NO : YES);
}

- (BOOL)getEISAId: (unsigned int *)_id  forSlot: (int) slot
{
	return eisa_id(slot, _id);
}

/*
 * DMA lock package. 
 */
 
typedef struct _dmaQueueEntry dmaQueueEntry;
struct _dmaQueueEntry {
	id		device;
	int		count;
	dmaQueueEntry	*next;
};

static dmaQueueEntry *dmaQueueHead;
static dmaQueueEntry *dmaQueueTail;
static int sleepChannel;			// for sleep/wakeup
static simple_lock_t dmaSpinLock;		// ditto

int dmaLockDisable = 0;

extern void thread_wakeup(int x);

/*
 * Init routine, called out from dev_server_init().
 */
void initDmaLock()
{
	dmaQueueHead = NULL;
	dmaQueueTail = NULL;
	dmaSpinLock  = simple_lock_alloc();
	simple_lock_init(dmaSpinLock);
}

/*
 * Sleep until specified dmaQueueEntry is at the head of dmaQueue.
 * Note we have to wait for a specific dmaQueueEntry to bubble to the head,
 * not just any dmaQueueEntry for a specific device, since one device can have 
 * one entry at the head and another somewhere back in line. 
 *
 * dmaSpinLock held on entry and exit.
 */
static void waitTilHead(dmaQueueEntry *queueEntry)
{
	while(queueEntry != dmaQueueHead) {
		thread_sleep((int)&sleepChannel, dmaSpinLock, FALSE); 
		simple_lock(dmaSpinLock);
	}
}

/*
 * Cons up a new dmaQueueEntry, count of 1, for specified device.
 */
static inline dmaQueueEntry *queueEntryAlloc(id device)
{
	dmaQueueEntry *queueEntry = IOMalloc(sizeof(dmaQueueEntry));
	
	queueEntry->count  = 1;
	queueEntry->device = device;
	queueEntry->next   = NULL;
	return queueEntry;
}

/*
 * Free a dmaQueueEntry.
 */
static inline void queueEntryFree(dmaQueueEntry *queueEntry)
{
	ASSERT(queueEntry->count == 0);
	IOFree(queueEntry, sizeof(dmaQueueEntry));
}

/*
 * Acquire dma lock. Allows multiple threads associated with one device
 * instance to hold the DMA lock siimultaneously. If calling device
 * owns the lock but other devices are blocked waiting for it, calling 
 * thread will block, and will not acquire the lock until all other pending
 * requests are honored.
 */
- (void)reserveDMALock
{
	dmaQueueEntry *queueEntry;
	dmaQueueEntry *newQueueEntry;
	
	if([self isEISAPresent] || dmaLockDisable) {
		return;
	}
	newQueueEntry = queueEntryAlloc(self);
	simple_lock(dmaSpinLock);
	if(dmaQueueHead == NULL) {
		/*
		 * Trivial quiescent case.
		 */
		dmaQueueHead = dmaQueueTail = newQueueEntry;
		simple_unlock(dmaSpinLock);
		return;
	}
	queueEntry = dmaQueueHead;
	if((queueEntry->device == self) && (queueEntry->next == NULL)) {
	   	/*
		 * This device owns the lock, and no other requests pending.
		 */
		queueEntry->count++;
		simple_unlock(dmaSpinLock);
		queueEntryFree(newQueueEntry);
		return;
	}
	
	/*
	 * See if any subsequent entries (after the first one) match this
	 * device.
	 */
	queueEntry = queueEntry->next;
	while(queueEntry) {
		if(queueEntry->device == self) {
			/*
			 * Calling device has a request pending, not at head 
			 * of queue. Lump these together.
			 */
			queueEntry->count++;
			waitTilHead(queueEntry);
			simple_unlock(dmaSpinLock);
			queueEntryFree(newQueueEntry);
			return;
		}
		queueEntry = queueEntry->next;
	}
	
	/*
	 * New request for this device.
	 */
	dmaQueueTail->next = newQueueEntry;
	dmaQueueTail = newQueueEntry;
	waitTilHead(newQueueEntry);
	simple_unlock(dmaSpinLock);
	return;
}

- (void)releaseDMALock
{
	dmaQueueEntry *queueEntry = dmaQueueHead;
	dmaQueueEntry *freeQueueEntry = NULL;
	boolean_t needWakeup = FALSE;
	
	if([self isEISAPresent] || dmaLockDisable) {
		return;
	}
	simple_lock(dmaSpinLock);
	if(queueEntry->device != self) {
		simple_unlock(dmaSpinLock);
		IOLog("%s: releaseDmaLock when not holding lock\n", 
			[self name]);
		panic("releaseDMALock");
	}
	if(--queueEntry->count == 0) {
		/*
		 * This device is done. Wake up others, if any.
		 */
		needWakeup = (queueEntry->next != NULL);
		freeQueueEntry = queueEntry;
		dmaQueueHead = queueEntry->next;
	}
	simple_unlock(dmaSpinLock);
	if(needWakeup) {
		thread_wakeup((int)&sleepChannel);
	}
	if(freeQueueEntry != NULL) {
		queueEntryFree(freeQueueEntry);
	}
}

/*
 * Support for the extended mode register (EISA only).
 */

- (IOReturn)setDMATransferWidth	: (IOEISADMATransferWidth)width
                     forChannel : (unsigned)localChannel
{
	unsigned int realChannel;
	int dmaWidth;
	
	if((width == IO_16BitWordCount) || is_ISA) {
		return IO_R_UNSUPPORTED;
	}
	if (localChannel >= [[self deviceDescription] numChannels]) {
		return IO_R_INVALID_ARG;
	}
	realChannel = [self _localToChannel:localChannel];

	switch(width) {
	    case IO_8Bit:
	    	dmaWidth = DMA_XFR_8_BIT;
		break;
	    case IO_16BitWordCount:
	    	dmaWidth = DMA_XFR_16_BIT_WORD;
		break;
	    case IO_16BitByteCount:
	    	dmaWidth = DMA_XFR_16_BIT_BYTE;
		break;
	    case IO_32Bit:
	    	dmaWidth = DMA_XFR_32_BIT;
		break;
	    default:
		return IO_R_INVALID_ARG;
	}
	dma_xfer_width(realChannel, dmaWidth);
	return IO_R_SUCCESS;
}

- (IOReturn)getDMATransferWidth	: (IOEISADMATransferWidth *)width_p
                     forChannel : (unsigned)localChannel
{
	unsigned int realChannel;
	int width;
	
	if (localChannel >= [[self deviceDescription] numChannels]) {
		return IO_R_INVALID_ARG;
	}
	realChannel = [self _localToChannel:localChannel];

	width = get_dma_xfer_width(realChannel);
	switch(width) {
	    case DMA_XFR_8_BIT:
	    	*width_p = IO_8Bit;
		break;
	    case DMA_XFR_16_BIT_WORD:
	    	*width_p = IO_16BitWordCount;
		break;
	    case DMA_XFR_16_BIT_BYTE:
	    	*width_p = IO_16BitByteCount;
		break;
	    case DMA_XFR_32_BIT:
	    	*width_p = IO_32Bit;
		break;
	    default:	/* should never be reached */
		return IO_R_NOT_ATTACHED;
	}
	return IO_R_SUCCESS;
}

/*
 * Select DMA Timing.
 */
- (IOReturn)setDMATiming	: (IOEISADMATiming)timing
                     forChannel : (unsigned)localChannel
{
	unsigned int realChannel;
	int dmaTiming;
	
	if(is_ISA) {
		return IO_R_UNSUPPORTED;
	}
	if (localChannel >= [[self deviceDescription] numChannels]) {
		return IO_R_INVALID_ARG;
	}
	realChannel = [self _localToChannel:localChannel];

	switch(timing) {
	    case IO_Compatible:
	    	dmaTiming = DMA_TIMING_COMPAT;
		break;
	    case IO_TypeA:
	    	dmaTiming = DMA_TIMING_A;
		break;
	    case IO_TypeB:
	    	dmaTiming = DMA_TIMING_B;
		break;
	    case IO_Burst:
	    	dmaTiming = DMA_TIMING_BURST;
		break;
	}
	dma_timing(realChannel, dmaTiming);
	return IO_R_SUCCESS;
}

/*
 * Select whether EOP pin is output (default) or input.
 */
- (IOReturn)setEOPAsOutput	: (BOOL)flag
                     forChannel	: (unsigned)localChannel
{
	unsigned realChannel;
	unsigned dmaFlag;
	
	if(is_ISA) {
		return IO_R_UNSUPPORTED;
	}
	if (localChannel >= [[self deviceDescription] numChannels]) {
		return IO_R_INVALID_ARG;
	}
	realChannel = [self _localToChannel:localChannel];

	dmaFlag = (flag ? DMA_EOP_OUT : DMA_EOP_IN);
	dma_eop_in(realChannel, dmaFlag);
	return IO_R_SUCCESS;
}

/*
 * Enable Stop register. Default is disabled; enabling this feature
 * is not supported in [PR2]the current release.
 */
- (IOReturn)setStopRegisterMode : (IOEISAStopRegisterMode)mode
                    forChannel 	: (unsigned)localChannel
{
	unsigned realChannel;
	unsigned dmaFlag;
	
	if((mode == IO_StopRegisterEnable) || is_ISA) {
		return IO_R_UNSUPPORTED;
	}
	if (localChannel >= [[self deviceDescription] numChannels]) {
		return IO_R_INVALID_ARG;
	}
	realChannel = [self _localToChannel:localChannel];

	if(mode == IO_StopRegisterEnable) {
		dmaFlag = DMA_STOP_ENABLE;
	}
	else {
		dmaFlag = DMA_STOP_DISABLE;
	}
	dma_stop_enable(realChannel, dmaFlag);
	return IO_R_SUCCESS;
}

- (IOReturn) reservePortRange	: (unsigned int) localPortRange
{
	return IO_R_SUCCESS;
}

- (void) releasePortRange	: (unsigned int) localPortRange
{
}

@end

unix.superglobalmegacorp.com

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