[comp.unix.questions] Sun VMEbus memory mapping for video frame grabber

ycy@walt.cc.utexas.edu (Joseph Yip) (02/27/90)

We bought  a frame grabber from Imaging Technology Inc. for our Sun 4
(VMEbus).  The board is supposed to come with the Toolbox (free) which
contains some programs on programming the frame grabber. But, Imaging
Tech. Inc. (ITI) stopped shipping the Toolbox AFTER we bought it! In the
diagnostic tape, I found this map.c source file which I think will help me
to program the interface between the Sun and the frame grabber. There are
a few questions I want to ask concerning interfacing with the VMEbus and 
external hardware. First, I will describe what the frame grabber is like.

- VMEbus standard 16-bit data bus, 24-bit address bus
- 512 Kbytes of frame memory are memory mapped at one time
- frame memory and look-up tables are mapped into VMEbus standard
  (24-bit address) non-privileged and supervisory address space.
- Registers are mapped into short (16-bit address) non-privileged and
  supervisory address space. 

Factory setting:
	Register base address is set to 0x1000. All registers are I/O mapped
		relative to this base address. The registers occupy 
		16 words within this space.

	Memory base address is set to 0xA00000

The frame memory and look-up tables (LUTs) are memory-mapped, occupying 512K
bytes within the host computer memory address space, giving the host direct
access to the 512K bytes of memory. The LUTs are mapped into the same memory
space as the frame memory. During LUT addressing only the lower 32K
bytes are used. 

The 16 registers that are I/O mapped occupying 16 words in the host computer
address space are used to program the frame grabber functions. So, to use the
frame grabber I need to handle the memory mapping. Once I can read and write to
registers and read and write to frame memory, I am home free!

The questions are as follows:
- 	what are the /dev/vme16d16 and /dev/vme24d16 device drivers? How do
	they work? /dev/vme16d16 associates with I/O and /dev/vme24d16
	associated with the memory. 

-	How does the mmap() work? The function returns the logical address.
	How is the logical address be useful? How does the mmap() function

-	mmap() takes 6 parameters. The "flags" parameter needs to be either
	MAP_SHARED, MAP_FIXED, ... How do the MAP_SHARED and MAP_FIXED work?
	work in mappng the register base address and the memory base address?

-	In doing the mapping, why do we need to concern with the pagesize of
	the system? Or even the page memory alignment.

-	if the frame memory is memory mapped and if MAP_SHARED is set, then
	changes in the frame memory or changes in the mapped memory space 
	should reflect changes in the corresponding mapped space?
	
-	the map() (from map.c) below opens the appropriate device driver,
	performs the mapping and returns the logical address. So, if I do

		unsigned char *regbase;

		/* IO indicates I/O mapping, 0x1000 = register base
		   32 = there are 16 registers (16 bit each)
		*/
		regbase = map(IO, 0x1000, 32);

		/* if (regbase + 8) = status/control register, for frame
		   grabbing, snapping, ... 
		*/

		*(regbase +  8) |= (SNAP);	/* set bit to snap a frame */
	

	should work. Right?

Thank you.

Joseph Yip
---------------------------------------------------------------------------
/* the following is part of the map.c file */

/*
 * map.c	Map bus address space to physical address space
 *		and return the base logical address of the mapped block.
 *	
 *      Synopsis:	BYTE *map( mio, physaddr, size )
 *				short		mio;
 *				DWORD		physaddr;
 *				DWORD		size;
 *
 *			physaddr is a physical address.
 *
 *			mio = IO (0)  if physaddr is an I/O address.
 *			mio = MEM (1) if physaddr is a memory address.
 *
 *			size is the size ( in bytes ) of the block.
 *
 *			BYTE is typedef'd to be an unsigned 8-bit type
 *
 *			DWORD is typedef'd to be an unsigned 32-bit type
 */


static	struct {
	char	*iodevice;	/* name of I/O space device driver */
	char	*memdevice;	/* name of Memory space device driver */
} bus[] = {

	"/dev/vme16d16",	"/dev/vme24d16",
	"/dev/vme16",		"/dev/vme24",

	0,			0
};


static int		BUS_FD;		/* file descriptor for the bus device */
static int		IOBUS, MEMBUS;	/* used by unmap() */
static DWORD		logaddr;	/* page-aligned logical base address */
static DWORD		psize;		/* determine system page size */
static DWORD		raw_logaddr;	/* unaligned logical base address */
static DWORD		ioraw_logaddr, memraw_logaddr;	/* used by unmap() */
static DWORD		length;		/* page-aligned block size */
static int		nomap = 0;	/* debug flag */


BYTE *map( mio, physaddr, size )
	short		mio;		/* memory or I/O space */
	DWORD		physaddr;	/* physical base address */
	DWORD		size;		/* size of the mapped block needed */
{
	DWORD		offset;		/* offset of physaddr */
					/* from nearest page boundary */
	char		s[80];		/* message buffer */
	int		b;		/* bus[] index */
	int		busfound;	/* bus "found" flag */
	DWORD		v;		/* mmap() return value */

	psize = getpagesize();		/* determine system page size */

	/*
	 * open the appropriate backplane bus device driver:
	 *
	 *	- search through list of backplane bus device drivers
	 *	- if unsuccessful in opening any device drivers, then 
	 *	  report error and die
	 */

	for (b = 0, busfound = 0; bus[b].iodevice != (char *)0; b++) {
		BUS_FD = open( (mio == 0) ? bus[b].iodevice : bus[b].memdevice,
				2 );
		if (BUS_FD >= 0) {
			/* save file descriptor for use by unmap() */
			if (mio == 0) 
				IOBUS = BUS_FD;
			else 
				MEMBUS = BUS_FD;
			busfound = 1;
			break;
		}
	}
	if (!busfound && (nomap == 0)) {
		fprintf( stderr,
		" Can't open %s space device driver. \n\n",
		(mio == 0) ? "I/O" : "Memory" );
		fprintf( stderr,
		" Read/Write permissions have not been enabled for the \n" );
		fprintf( stderr,
		" appropriate backplane bus device drivers in your system. \n");
		fprintf( stderr,
		"\n Issue the command \"chmod a+rw /dev/%s\" as superuser. \n",

		"vme{16,24}d16" );

	}

	/*
	 * allocate two more pages than needed to guarantee
	 * enough page-aligned memory blocks
	 */

 	raw_logaddr = (DWORD) malloc((unsigned)(size + psize + psize));

	/* save address for use by unmap() */
	if (mio == 0)
 		ioraw_logaddr = raw_logaddr;
	else	/* MEM */
 		memraw_logaddr = raw_logaddr;

	/* align pointer on page boundary */

	logaddr = ((raw_logaddr + psize) & ~(psize - 1));
	offset = physaddr - (physaddr & ~(psize - 1));
	physaddr -= offset;
	length = (size + offset + (psize - 1)) & ~(psize - 1);

	if (nomap == 1)		/* skip physical address mapping */
		return (BYTE *)((DWORD)logaddr + offset);

	v = (DWORD)mmap(logaddr,length,(0x1|0x2),(1|0x10),BUS_FD,physaddr);
	printf("mmap(0x%lX, 0x%lX, %u, %u, %u, 0x%lX) returned 0x%lX\n",
		  logaddr, length, (0x1|0x2), (1|0x10), BUS_FD, physaddr, v );
	if (v == (-1)) {
		perror("mmap");		/* print system error message */
		sprintf( s,"mmap: Can't map %lXH bytes at %s address %8lXH\n\n",
			length, (mio == 0) ? "I/O" : "Memory", physaddr );
	}

	return (BYTE *)((DWORD)logaddr + offset);
}


unmap( mio )	/* Unmap last I/O or MEMORY region mapped */
	short		mio;		/* memory or I/O space */
{
	char		s[80];		/* message buffer */

	if (munmap( logaddr, length ) != 0) {
		sprintf( s, "munmap: Can't unmap %lx bytes from %s space\n\n",
			length, (mio == 0) ? "I/O" : "Memory" );
	}

	if (mio == 0) {
 		free(ioraw_logaddr);	/* Deallocate virtual storage space */
		if ( !nomap )
			close(IOBUS);	/* close bus device driver */
	} else {	/* MEM */
 		free(memraw_logaddr);	/* Deallocate virtual storage space */
		if ( !nomap )
			close(MEMBUS);	/* close bus device driver */
	}
}


disable_map()	/* disallow physical address mapping by map() */
{
	if (nomap) return;

	/* make sure this action does not go unnoticed */
	printf( "\n" );
	printf( "  WARNING:  Physical Address Mapping Disabled !!!  " );
	printf( "\n\n" );
	nomap = 1;
}