[net.unix-wizards] DR11-W to Model-1/20 on 4.1 BSD

romig@osu-dbs.UUCP (12/06/83)

We have a Raster Technologies Model-One/20 frame buffer that I am
*trying* to connect to our Vax 780 (which runs 4.1 BSD) with a DR11-W.
The driver (which I hacked from a DR11-B/W driver that hadn't been
tested with a DR11-W) *seems* to work, although I am not sure.
Oh yeah, the DR11-W is the MDB DR11-W, not the DEC DR11-W.

Specifically, I load the CSR with function bit 3 set, set the GO bit,
and it times out N seconds later.  The function bits appear to get
set correctly, the cables seem to be hooked up correctly, and I'm
really stumped.

(1) Has anyone out there connected a Model One/xxx to a Vax running
    Unix with a DR11-W before?  Can you give me any helpful hints (or
    even software?)

(2) Does anyone out there have any helpful hints on what I can do to 
    find the problem?  General debugging techniques for Unix device
    drivers would be *VERY* much appreciated.  Specific debugging hints
    for DR11-W's would be nice too.  For example, is it possible for me
    to use adb to massage the device registers and force it to do a write
    operation?  Can anyone clue me in on how to do this (ie, what to write
    where...)

Sigh....whimper....

					Steve Romig (614) 422-0915
					cbosgd!osu-dbs!romig (UUCP)
					romig@ohio-state (CSNET)

padpowell@wateng.UUCP (PAD Powell [Admin]) (12/09/83)

The first thing you should do is take the far end of the device off,
and put a loop-back cable on. This can be done by using a single ttl
gate, 74ls00, with the data/out strobe being buffered and drive the acknowledge.
Ask your local hardware type to whip this up, should take about 3 minutes,
if you are crude, and vulgar.  Now, tie a monostable onto the fool thing,
to generate a pulse (drive an LED) each time you get a data transfer.
If you put a counter on the end of it, you can also get a count of the
number of pulses.  

Now, you are in shape to debug your driver.   Work on DMA out first,
then work on DMA in.  If you are clever, your hardware guy will hook up
a couple of counters to the input signals.  Now you can do most of the
debugging.

Warning:
	Most of the times you hang is cause you are sending the wrong
number of bytes to the destination.  Read the documentation on the
value to be loaded into the Byte/Word Count register carefully.
MAKE DAMN SURE YOU HAVE NOT GOT SIGNED/UNSIGNED arithmetic problems.
Finally,  are you sure that you loaded the register?  Check the compilation
assembly for the instructions,  and make sure you are loading with the
right register.

Patrick ("I hates DMA Devices forever") Powell

rpw3@fortune.UUCP (12/17/83)

#R:osu-dbs:-42800:fortune:11600029:000:5624
fortune!rpw3    Dec 17 01:44:00 1983

(I also hate DMA devices, Patrick!) The following technique may prove
helpful to someone.

We hooked a 4.1 VAX to another (guess which) machine using a DR-11C,
and the most useful thing was a little peek/poke program that used
/dev/kUmem to bang the registers before the driver was ever written.

	***WARNING*** If you do this, make DAMN SURE you don't
	***WARNING*** turn on the interrupt enable bits by
	***WARNING*** mistake. (Crash, tinkle, tinkle,...)

Having gotten that to work (without ever crashing anything -- didn't
mean to scare you, just to warn), and therefore knowing the hardware
path and the other end were o.k., the next thing was to write the
more-or-less complete driver and check it out in user mode (as
subroutines to the peek/poke program), using the following trick:

	Your driver will have a struct which describes the registers, and
	a register variable that will hold the address of the registers
	at run time, so that you read data with "x = dr->dr_Rdata", for
	example. (See any UNIX driver.)

	When debugging in user mode, you make 'dr' point to an actual
	data struct in your program, which your driver code cheerfully
	reads and writes just as if it were the real hardware registers.
	[Here's the trick] At "appropriate" places in the driver, put
	macros called (for example) DR_GET and DR_PUT which read and
	write "/dev/kUmem" into/out-of the in-core copy. ("Appropriate"
	means whenever you need a change manifested from/to the outside
	world, see example, below.) After you have successfully gotten
	the driver algorithms debugged, the macro definitions get
	nulled, and you drop the driver into the kernel.

This was so successful that we only had to take the system down three
times: one to install the board, and twice to boot new kernels
(yes, there was one bug left, dammit, having to do with the squirrily
4.1 "auto-config").

Needless to say, the same technique was also used on the other machine,
which in fact never went to a kernel driver since that machine had a
version of the old "phys()" system call.  The hardware was just mapped
into user space, avoiding the kUmem reads and writes.

	<<FLAME ON>> I would not have bothered on the VAX, either, but
	4.1 gave me no easy (or possible?) way to map the UNIBUS
	addresses into user virtual space.  With a "phys"-equivalent,
	and a reasonable scheduler, most device-driver writing (and
	therefore system downtime) for random hack devices can be
	avoided. <<FLAME OFF>>

Fine points:

1. You will need to know the /dev/kUmem address for your board, which
can be gotten by looking at the addresses printed out by some other
device's "probe" routine. If needed, write a stub driver to print it
out. (I never figured out how to predict it from the config. in 4.1.)

2. The kUmem version of our link ran at about 600 transfers/sec (each
GET (or PUT) was one seek and one read (or write)), while the driver version
ran at about 20-25000/sec, since it touched memory directly. <<FLAME ON/OFF>>

3. Extending the approach to handle (pseudo)interrupts is a little more
work, but not impossible. The test-bed program just has to continually
do GET's until the status changes, and then call the interrupt routine.
(But I don't have a strong enough heart or a weak enough mind to
try it on DMA!)

Good luck!

Rob Warnock

UUCP:	{sri-unix,amd70,hpda,harpo,ihnp4,allegra}!fortune!rpw3
DDD:	(415)595-8444
USPS:	Fortune Systems Corp, 101 Twin Dolphins Drive, Redwood City, CA 94065

-------- edited (censored) excerpts from the user-mode version --------------

/* hack to test dr11 through kUmem */

typedef struct dr11c_s {
			short		dr_csr;
			unsigned short	dr_Tdata;
			unsigned short	dr_Rdata;
		} dr11c_t;

dr11c_t	dr_;		/* local copy, to avoid so many kUmem accesses */

dr11c_t *dr = &dr_;	/* so it looks like the production pointer code */

#define	DR_KVADDR 0x800e0ff8	/* address in kernel through kUmem */
				/* WILL VARY BY SYSTEM */

/*
 * These routines update the in-memory copy
 */
#define	DR_GET	dr_rd();/* update local copy from kUmem */
#define	DR_PUT	dr_wr();/* flush local copy back to kUmem */
dr_rd()
{

	lseek(dr_fd,DR_KVADDR,0);
	if(read(dr_fd,(char *)dr,6) != 6){	/* **HACK** machine dependent */
		printf("[ dr_rd failed!]\n");
		exit(1);			/* aaaUUUUgah, die! die! */
	}
}

dr_wr()
{
	lseek(dr_fd,DR_KVADDR,0);
	dr->dr_csr &= ~(DR_IENABA | DR_IENABB); /* make no waves! */
	if(write(dr_fd,(char *)dr,6) != 6){	/* **HACK** machine dependent */
		printf("[ dr_wr failed!]\n");
		exit(1);
	}
}


int	dr_fd;	/* fildes for kUmem */

main(argc,argv,env)
int argc; 
char **argv, **env;
{	int	i, val, addr, tseq, rseq, otseq, orseq;

	if( (dr_fd = open("/dev/kUmem",2)) < 0) {
		printf("[ can't get kUmem!\n");
		exit(1);
	}

	DR_GET		/* see what's out there */

	connect();	/* try to connect */

 	transfer();	/* move the data */

	disconnect();	/* ensure orderly end */

}

connect()
{	int	i;

	DR_GET
	dr->dr_data = PK_RESET;	/* try out connection with a reset */
	DR_PUT				/* Go! */

	for(i = PK_INITTO; i > 0; i--){
	    DR_GET
	    if(
		(dr->dr_data == PK_RESET)
	      )		break;
	    else	sleep(1);
	}
	if(i <= 0){
		dr->dr_Tflag &= ~DR_CFLG;
		DR_PUT
		printf("[ Initial connection attempt failed!]\n");
		exit(1);
	}
	printf("[ Connect]\n");
}

transfer()
{
	int tikto;
	register int c;

	dr->dr_Tdata &= ~DR_CFLG;	/* start sending data */
	DR_PUT

	while((c = getchar()) != EOF){
		tikto = 5000;
		while(1){
			DR_GET
			"if dr->Rdata looks ready, break;"
			if(--tikto < 0)
				sleep(1);
		}
		dr->dr_Tdata = c;
		dr->dr_Tdata ^= DR_TSEQ;
		DR_PUT
	}
}
---------------- end of edited excerpt ----------------

thomas@utah-gr.UUCP (Spencer W. Thomas) (01/02/84)

Using /dev/kUmem is a really good way to crash the system.  If you try
to touch a non-existent address, the system crashes (at least our 4.1
system did).  Not too healthy.  Besides, you can write all over ANYTHING
with it.  Not too good for system security.  I mean, suppose you
accidently write on the disk drive registers?

=S