[comp.unix.i386] EGA memory and i/o access under System V/386

frk@frksyv.UUCP (Frank Korzeniewski) (06/13/89)

I read article <108512@sun.Eng.Sun.COM> by John Plocher with high hopes.
The subject says you can map physical memory into a process's context.
I hoped it would be a better technique than the one I had been using.
Well, I like mine better. The following lets you access the EGA memory AND
i/o ports from an application process. If anyone has any better ideas
please let me know.

This code depends on HARDWARE data structures defined in the 386 and so
should run on all ATT System V/386 Unices.

Have fun using it.

====================== START OF DRIVER ==========================
/*
 * EGMDVR.C
 */

extern unsigned char *sptalloc ();

#include <sys/types.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/cmn_err.h>
#include <sys/dir.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/sysmacros.h>
#include <sys/immu.h>
#include <sys/tss.h>

/*
 * define the driver ioctl functions
 */
#define	EGM_GET_PARMS	(('G' << 8) | 'P')	/* get parms */

/*
 * returned structure from driver
 */
struct egm_area
{
	unsigned char *mt_mem;		/* pointer to text screen memory */
	unsigned char *mg_mem;		/* pointer to graphic screen memory */
	unsigned char *rom_p;		/* pointer to ega bios rom */
};

/*
 * EGA memory areas and sizes
 */
#define	MG_SIZE		0x10000
#define	MG_ADDR		0xa0000
#define	MT_SIZE		0x10000
#define	MT_ADDR		0xb0000
#define	ROM_SIZE	0x10000
#define	ROM_ADDR	0xc0000

struct egm_area usr;

int	opened = 0;

	void
egm_open (dev_num, flags, otyp)
	dev_t	dev_num;
	int	flags, otyp;
{
	if (opened || alloc_parms ())
	{
		u . u_error = EINVAL;
		return;
	}
	opened = 1;
}

	void
egm_write (dev_num)
	dev_t	dev_num;
{
	u . u_error = EINVAL;
}

	void
egm_ioctl (dev_num, cmd, arg, mode)
	dev_t	dev_num;
	int	cmd, arg, mode;
{
	int	i, oldlev;
	pde_t	*pdp;

	if (!opened)
	{
		u . u_error = EINVAL;
		return;
	}

	switch (cmd)
	{
	case EGM_GET_PARMS :
		/* give screen and rom addresses to caller */
		copyout (&usr, arg, sizeof (usr));
		oldlev = spl7 ();

		/* turn on user bit in page table dir */
		pdp = (pde_t *) (_cr3 () - 0x40000000);
		i = (((int) usr . mt_mem) >> PTNUMSHFT) & PTOFFMASK;
		pg_setprot (pdp + i, PG_US | PG_RW);
		i = (((int) usr . mg_mem) >> PTNUMSHFT) & PTOFFMASK;
		pg_setprot (pdp + i, PG_US | PG_RW);
		i = (((int) usr . rom_p) >> PTNUMSHFT) & PTOFFMASK;
		pg_setprot (pdp + i, PG_US | PG_RW);

		/* enable i/o instructions */
		u . u_tss -> t_bitmapbase = 0x680000;

		splx (oldlev);
		break;
	default :
		u . u_error = EINVAL;
		return;
	}
}

	int
alloc_parms ()
{
	unsigned char	*ids;
	int	idm, try;

	/*
	 * map the EGA graphics memory segment into the process address space
	 */
	try = 120;	/* number of tries to get it 64k aligned */
	while (1)
	{
		/*
		 * try to alloc (we need it 64k aligned)
		 */
		usr . mg_mem = sptalloc (btoc (MG_SIZE),
			PG_P | PG_LOCK | PG_US | PG_RW, btoc (MG_ADDR), 0);
		if (usr . mg_mem == 0)
		{
			u . u_error = EFAULT;
			return (1);
		}

		/* if aligned then we are done */
		if (((int) usr . mg_mem & 0xffff) == 0)
			break;

		/* get filler size needed and free first copy */
		idm = (int) usr . mg_mem & 0xffff;
		sptfree (usr . mg_mem, btoc (MG_SIZE), 0);

		/* allocate filler first */
		ids = sptalloc (btoc (idm),
			PG_P | PG_LOCK | PG_US | PG_RW, btoc (MG_ADDR), 0);
		if (ids == 0)
		{
			u . u_error = EFAULT;
			return (1);
		}

		/* now try to allocate 64k aligned again */
		usr . mg_mem = sptalloc (btoc (MG_SIZE),
			PG_P | PG_LOCK | PG_US | PG_RW, btoc (MG_ADDR), 0);
		sptfree (ids, 0);
		if (usr . mg_mem == 0)
		{
			u . u_error = EFAULT;
			return (1);
		}

		if (((int) usr . mg_mem & 0xffff) != 0)
		{
			/*
			 * still not aligned, so free, delay, try again
			 */
			sptfree (usr . mg_mem, btoc (MG_SIZE), 0);
			if (--try == 0)
			{
				/* tries exceeded, so fail */
				u . u_error = EFAULT;
				return (1);
			}
			delay (2);
		}
		else
			break;		/* got it 64k aligned */
	}

	/*
	 * map the EGA text memory into the process address space
	 */
	usr . mt_mem = sptalloc (btoc (MT_SIZE),
		PG_P | PG_LOCK | PG_US | PG_RW, btoc (MT_ADDR), 0);
	if (usr . mt_mem == 0)
	{
		sptfree (usr . mg_mem, btoc (MG_SIZE), 0);
		u . u_error = EFAULT;
		return (1);
	}

	/*
	 * map the EGA rom memory into the process address space
	 */
	usr . rom_p = sptalloc (btoc (ROM_SIZE),
		PG_P | PG_LOCK | PG_US | PG_RW, btoc (ROM_ADDR), 0);
	if (usr . rom_p == 0)
	{
		sptfree (usr . mg_mem, btoc (MG_SIZE), 0);
		sptfree (usr . mt_mem, btoc (MT_SIZE), 0);
		u . u_error = EFAULT;
		return (1);
	}
	return (0);	/* it all worked */
}

	void
egm_close (dev_num, flags, otyp)
	dev_t	dev_num;
	int	flags, otyp;
{
	sptfree (usr . mg_mem, btoc (MG_SIZE), 0);
	sptfree (usr . mt_mem, btoc (MT_SIZE), 0);
	sptfree (usr . rom_p, btoc (MT_SIZE), 0);
	opened = 0;
}

========================= END OF DRIVER =============================
========================= START OF APPLICATION CODE =================
/*
 * define the driver ioctl functions
 */
#define	EGM_GET_PARMS	(('G' << 8) | 'P')	/* get parms */

/*
 * returned structure from driver
 */
struct egm_area
{
	unsigned char *mt_mem;		/* pointer to text screen memory */
	unsigned char *mg_mem;		/* pointer to graphic screen memory */
	unsigned char *rom_p;		/* pointer to ega bios rom */
};

int	gfd;

/*
 * when user mode seg violations occure because we were paged out
 * and back in, we must then remap the EGA pages to our address space
 */
	void
restore_map ()
{
	struct egm_area *ega;

	if (ioctl (gfd, EGM_GET_PARMS, ega) == -1)
	{
		perror ("ioctl error");
		exit ();
	}

	signal (SIGSEGV, restore_map);
}

/*
 * put screen in graphics mode
 */
	void
graphics (ega)
	struct egm_area *ega;
{
	if ((gfd = open ("/dev/egm", O_WRONLY, 0)) == -1)
	{
		perror ("open error");
		exit ();
	}

	if (ioctl (gfd, EGM_GET_PARMS, ega) == -1)
	{
		perror ("ioctl error");
		exit ();
	}

	signal (SIGSEGV, restore_map);

	/* ... insert code to access EGA registers and memory to init it */
}
========================= END OF APPLICATION CODE =======================

-- 
______________________________________________________________________________
||  Frank Korzeniewski, Consulting                 Suite 137                ||
||  Phone: (415) 799-1819                          1564-A Fitzgerald Drive  ||
||  UUCP: uunet!frksyv!frk                         Pinole, CA 94564         ||