File:  [Apple Darwin 0.x] / driverkit / Examples / loadable / Kernel / myDevice.m
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:37:50 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@
 */
/*	IOStub.m	1.0	01/29/91	(c) 1991 NeXT   
 *
 * IOStub.m - Implementation for trivial I/O device subclass.
 *
 * HISTORY
 * 29-Jan-91    Doug Mitchell at NeXT
 *      Created. 
 */
 
#import "myDevice.h"
#import <kernserv/queue.h>
#import <kernserv/prototypes.h>
#import <machkit/NXLock.h>
#import <architecture/nrw/io.h>
#import <architecture/nrw/dma_macros.h>
#import <driverkit/generalFuncs.h>
#import <driverkit/driverServer.h>

static volatile void myDeviceThread(id deviceId);

/*
 * Communication between exported threads and I/O thread via a queue
 * of these structs.
 */
typedef struct {
	NXConditionLock		*lock;		// caller sleeps on this
	myDeviceOp		op;		// operation to perform
	IOReturn		status;		// status on completion
	queue_chain_t		link;
} cmdBuf;

/*
 * States of cmdBuf.lock and of qLock.
 */
typedef enum {
	LS_IDLE,
	LS_BUSY
} lockState;

@implementation myDevice

/*
 * Set device port. devicePort must be a port_t in the IOTask IPC space.
 * This is called from myHandler, which is NOT in the IOTask context, hence
 * convertPortToIOTask() was used to generate the devicePort passed in here.
 */
- (void)setDevicePort : (port_t)devicePort
{
	myDevicePort = devicePort;
}

/*
 * Initialize. We assume that myDevicePort is valid at this time.
 * Again, this is called from myHandler's context, so we start up an I/O 
 * thread in the IOTask to enable performing actual I/O using the hardware
 * represented by myDevicePort. 
 */
- init
{
	/*
	 * Initialize resources.
	 */
	queue_init(&ioQueue);
	qLock = [NXConditionLock alloc];
	[qLock initWith:LS_IDLE];
	IOForkThread((IOThreadFunc)myDeviceThread, self);
	
	/*
	 * Send an initialize command to the I/O thread.
	 */
	[self enqueueCmd:OP_INIT];
	
	return self;
}

/*
 * Shut down, free resources (but not ourself).
 */
- (void)shutDown
{
	[self enqueueCmd:OP_SHUTDOWN];
	[qLock free];
}

/*
 * Private methods.
 *
 * Send a cmdBuf to the I/O thread and wait for completion. This is the
 * means by which exported methods (-init, -shutDown) pass commands to
 * the I/O thread.
 */
- (IOReturn)enqueueCmd : (myDeviceOp)op
{	
	cmdBuf cmd;
	
	/*
	 * Create a cmdBuf.
	 */
	cmd.lock = [NXConditionLock alloc];
	[cmd.lock initWith:LS_BUSY];
	cmd.op = op;
	
	/*
	 * Enqueue on ioQueue.
	 */
	[qLock lock];
	queue_enter(&ioQueue, &cmd, cmdBuf *, link);
	[qLock unlockWith:LS_BUSY];
	
	/*
	 * Wait for completion.
	 */
	[cmd.lock lockWhen:LS_IDLE];
	[cmd.lock free];
	return cmd.status;
}

/*
 * Initialize device, called from within the I/O thread.
 */
- (IOReturn)deviceInit
{
	IODevicePage *devicePage;
	IOReturn drtn;
	unsigned intr;
	
	/*
	 * Map in device Page.
	 */
	drtn = _IOMapDevicePage(myDevicePort,
		task_self(),
		(vm_offset_t *)&devicePage,
		YES,				// anywhere
		IO_CacheOff);
	if(drtn) {
		IOLog("myDevice: _IOMapDevicePage() returned %s\n", 
		    	[self stringFromReturn: drtn]);
		return IO_R_NO_MEMORY;
	}
	
	/*
	 * Make sure we can access device page.
	 */
	set_chan_intr_set(devicePage, 0, 0x5a);
	intr = chan_intr_cause(devicePage, 0);
	IOLog("myDevice: channel interrrupt cause 0x%x\n", intr);
		
	/*
	 * This should clear all cause bits.
	 */
	set_chan_intr_cause(devicePage, 0, 0xffff);
	intr = chan_intr_cause(devicePage, 0);
	IOLog("myDevice: channel interrrupt cause 0x%x\n", intr);
	set_chan_intr_mask(devicePage, 0, 0x55aa);
	intr = chan_intr_mask(devicePage, 0);
	IOLog("myDevice: channel interrrupt mask  0x%x\n", intr);
	return IO_R_SUCCESS;
}


/*
 * The I/O thread which performs all of the useful work of this example.
 */ 
static volatile void myDeviceThread(myDevice *deviceId)
{
	cmdBuf *cmdp;
	BOOL die = NO;
	queue_head_t *ioQ = &deviceId->ioQueue;
	
	IOLog("myDevice: I/O thread starting\n");
	
	while(1) {
	
		/* 
		 * Wait for something to do.
		 */
		[deviceId->qLock lockWhen:LS_BUSY];
		cmdp = (cmdBuf *)queue_first(ioQ);
		queue_remove(ioQ, cmdp, cmdBuf *, link);
		
		/*
		 * Leave qLock in appropriate state, depending on contents
		 * of queue.
		 */
		if(queue_empty(ioQ)) {
			[deviceId->qLock unlockWith:LS_IDLE];
		}
		else {
			[deviceId->qLock unlockWith:LS_BUSY];
		}
		
		/*
		 * Execute command.
		 */
		switch(cmdp->op) {
		    case OP_INIT:
		    	cmdp->status = [deviceId deviceInit];
			break;
			
		    case OP_SHUTDOWN:
		    	/*
			 * Kill this thread, but return good status first.
			 */
			die = YES;
			cmdp->status = IO_R_SUCCESS;
		}
		
		/*
		 * Return cmdBuf to caller.
		 */
		[cmdp->lock lock];
		[cmdp->lock unlockWith:LS_IDLE];
		
		/*
		 * Terminate if necessary.
		 */
		if(die) {
			IOLog("myDriver; I/O thread terminating\n");
			IOExitThread();
		}
	}
	
	/* Not reached */
}

unix.superglobalmegacorp.com

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