/*
 * semi-pseudocode for Unix block device driver
 */

/* device register address
*/
#define  BLOCKDEV	((struct device *)0177400)

/* device register control block. This structure
 * maps on the BLOCKDEV address and allows per register access.
 */
struct device {
	int	status;		/* controller status register */
	int 	command; 	/* controller command register */
	int	error;		/* error register */
	long 	addr;		/* address register */
	long	count;		/* transfer count */
}; 

struct buf	blockHeader;	/* device i/o queue */
struct buf	rawBuffer;	/* buffer needed for raw i/o interface */

/*
 * strategy routine. DRIVER ENTRY POINT.
 * queue buffers on local queue
 * and make sure device is "started"
 * i.e., interrupt driven i/o in progress.
 */
bkstrategy(bp)
struct buf *bp;
{
	.make sanity check on bp->b_blkno/device #
		may set error and return
	.zero out bp->av_forw since that will be end of block chain
	.raise priority to block local interrupt handler
	.put block on local device queue ( we may sort it with disksort() )
		if queue empty
			blockHeader.b_actf <- bp
		else
			blockHeader.b_actl->av_forw <- bp
		blockHeader.b_actl <- bp
	.if i/o NOT in progress
		call device start routine
	.restore priority
}

/*
 * "start" the device doing output.
 * setup hardware controller with next buffer
 * for i/o. 
 */
bkstart()
{
	if no buffers queued
		return
	.get next buffer
	.set started flag
	.map device/logical block # to local true device
		addresses (may use partition table here to
		find true absolute block)
	.setup hardware
	.fireup hardware
	(might be dma, might be copying bytes from buffer to
		internal staging area for more primitive hardware
		setups)
}

/*
 * device interrupt routine.
 * Called from assembler regions of kernel by device interrupt.
 * Priority level is set in hardware. 
 */
bkintr()
{
	.if not started, 
		return
	.get buffer at head of queue
	.set not busy
	.if hardware error
		call generic device error routine
		may reset device
		may try again
		set B_ERROR flag in buffer
	.set device block header to next buffer
		header.b_actf <- bp->av_forw
	.call local start routine again to process any
		more buffers in i/o queue (keep device going)
	.call buffer cache routine iodone(bp) to
		release buffer and wakeup any sleepers
}

/*
 * raw i/o interface. Call strategy routine
 * via common code physio() setup routine. Use raw buffer
 * since strategy/start interrupt routines are coded to assume
 * a buffer header. physio will setup address translation
 * for kernel to user direct data transfer.
 */
bkread(dev)
DEVICE	dev;
{
	physio(bkstrategy, &rawBuffer, dev, B_READ);
}

bkwrite(dev)
DEVICE	dev;
{
	physio(bkstrategy, &rawBuffer, dev, B_WRITE);
}

/*
 * typically nothing to do
 */
bkopen()
{
}

/*
 * nothing to do
 */
bkclose()
{
}

