Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Posting-Version: version B 2.10.1 6/24/83; site fortune.UUCP Path: utzoo!linus!security!genrad!grkermit!masscomp!clyde!floyd!harpo!seismo!hao!hplabs!hpda!fortune!rpw3 From: rpw3@fortune.UUCP Newsgroups: net.unix-wizards Subject: Re: DR11-W to Model-1/20 on 4.1 BSD - (nf) Message-ID: <2039@fortune.UUCP> Date: Sat, 17-Dec-83 11:05:28 EST Article-I.D.: fortune.2039 Posted: Sat Dec 17 11:05:28 1983 Date-Received: Tue, 20-Dec-83 01:35:41 EST Sender: notes@fortune.UUCP Organization: Fortune Systems, Redwood City, CA Lines: 189 #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. <> 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. <> 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. <> 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 ----------------