[comp.periphs] Microport DMA Device Drivers

ajs@killer.UUCP (Anthony Starks) (05/27/87)

What is the *correct* method for writing a device driver for a DMA
driven device under Microport System V/AT with a stock 8MHz IBM PC/AT?
My difficulty is in the correct mapping of physical addresses, and
moving the data to the user buffer. 

The approach I'm taking is to get the physical address of the user
buffer via physaddr() and mapin() then translating this 32 bit number
into the appropriate 64K word page and offset required by the Intel 8237
DMA controller.  The problem is that when the transfer is completed no
data has been sent to the buffer. 

Note that typically I will get a number like 0x36AF8F4 as the result of
physaddr(u.u_base) and 0x36A0000 as the result of
mapin(physaddr(u.u_base), UPAGE_SEL).  UPAGE_SEL is defined as 8 in
sys/mmu.h. 

So, I'm unclear as to what "magical" combination of physaddr() and
mapin() must used to get a "real live" physical address that can be used
by a device driver.

Below is a code fragment that illustrates the problem.


-------------------- Start of driver code fragment --------------------------
/*
 *  Copyright (c) 1987 Merrell Dow Research Institute
 *
 */
	.
	.
	.

#define LO(w)	((w) & 0x00ff)
#define HI(w)	LO((w) >> 8)
#define LOA(a)	((a) & 0x0000ffff)
#define HIA(a)	LOA((a) >> 16)
	.
	.
	.

/*
 *  DMA routine - start an i/o operation: do device register 
 *  manipulation and DMA programming
 */
dev_dma(dev, addr)
register int dev;
unsigned long addr;
{
	register unsigned int  dma_baddr;
	register unsigned char dma_page;

	.
	.
	.

	/*
	 *  Determine the DMA base address and DMA page from the
	 *  physical address.
	 *
	 *  The DMA controller sees memory in 64K word pages.
	 *  There are 125 pages numbered 0-124 in the 16 M byte address 
	 *  space of the iAPX 286. The DMA base address is the lowest memory
	 *  address on the designated page.
	 *
	 *  So, the DMA page is the low eight bits of the high 16 bits of the
	 *  physical address divided by two to convert from bytes to words.
	 *
	 *  The DMA base address is simply the low-order 16 bits of the 
	 *  physical address divided by two.
	 */


	dma_baddr = LOA(addr) >> 1;
	dma_page  = (HIA(addr) & 0x0ff) >> 1;
	.
	.
	.
	.	
	
	/*
	 *  Set up the DMA transfer
	 */
	 
#ifdef DEBUG	
	printf("dt: dma:\nValues\nsamples=%d\naddr=0x%lx\ndma_page=0x%x (%d)\ndma_baddr=0x%x\nDMA mode=%x\n",
		dtc.nsamples, addr, dma_page, dma_page, dma_baddr, dp->dma_mode);
	printf("Registers\nmreg=%x\tareg=%x\tcreg=%x\tpreg=%x\n",
		dp->dma_mreg, dp->dma_areg, dp->dma_creg, dp->dma_preg);
#endif

/*
 *	CBPFF2   == 0xD8
 *	dma_preg == 0x8B
 *	dma_areg == 0xC4
 *	dma_creg == 0xC6
 *	dma_mreg == 0xD6
 *	dma_mode == 0x45
 *	MASK2    == 0xD4
 */
	outb(CBPFF2,       (unsigned char)0x0);	/* Reset */
	outb(dp->dma_preg, dma_page);		/* Select page */
	outb(dp->dma_areg, LO(dma_baddr));	/* low byte of address */
	outb(dp->dma_areg, HI(dma_baddr));	/* high byte of address */
	outb(dp->dma_creg, LO(dtc.nsamples-1));	/* low byte of word count */
	outb(dp->dma_creg, HI(dtc.nsamples-1));	/* high byte of word count */
	outb(dp->dma_mreg, dp->dma_mode);	/* Write mode, no auto-init */
	outb(MASK2,        (unsigned char)0x1);	/* Enable */


#ifdef DEBUG
	printf("dt: dma: dma prog done\n");
#endif
	.
	.
	.
	.
}	

/* Read from the device  (A/D conversion) */

devread(dev)
dev_t dev;
{
	register struct dt_state *dp=dts;
	register int priority, nbytes;

	nbytes = ...

	/*
	 *  Check for a valid address
	 */
	if (useracc(u.u_base, nbytes, B_READ) == 0)
	{
		u.u_error = EFAULT;
		return;
	}

	/*
	 *  Do the data transfer:
	 *
	 *  (0) Record the processor priority.
	 *  (1) Set the sleeping flag.
	 *  (2) Lock the process in memory.
	 *  (3) Initiate the DMA transfer, directly to the user buffer.
	 *  (4) Sleep while the transfer takes place.
	 *  (5) When awakened by the interrupt, restore priority, 
	 *      and unlock the process.
	 */
	priority = spl4();
	dp->status |= SLEEP;
	
#ifdef DEBUG
	printf("dt: read: u = 0x%lx\nSleep\n", physaddr( &u ));
#endif

	u.u_procp->p_flag |= SLOCK;
	dev_dma(dev, mapin (physaddr (u.u_base), UPAGE_SEL)); /*** ??? ***/
	while (dp->status & SLEEP)
		sleep((caddr_t)&dp->status, PRI);
	splx(priority);
	u.u_procp->p_flag &= ~SLOCK;

#ifdef DEBUG
	printf("dev: read: Wakeup\n");
	printf("DMA address = 0x%x%x\n", inb(dp->dma_areg), inb(dp->dma_areg));
	printf("DMA count   = 0x%x%x\n", inb(dp->dma_creg), inb(dp->dma_creg));
#endif

	u.u_base   += nbytes;
	u.u_offset += nbytes;
	u.u_count  -= nbytes;
}
-------------------- End of driver code fragment --------------------------

Anthony J. Starks		...{ihnp4 | seismo}!iuvax!hpuinda!mdri!ajs
Merrell Dow Research Institute
P.O. Box 68470 
Indianapolis, IN 46268