arensb@cvl.umd.edu (Andrew Arensburger) (08/31/89)
Sun used to have a nice, well-behaved routine called 'mmap()', which took a block of memory, and mapped it to a device such as -- in my case -- a VMEbus. Sadly, they changed it with SunOS 4.0. Now, instead of returning an (integer) error code, it returns a pointer (or -1 in case of error. Aaarggghh!). It is unclear what the return value has to do with the pointer one has passed in, or indeed what the routine does, now. I need to map certain addresses on /dev/vme16 and /dev/vme24 to user memory. Does anyone have any experience with 'mmap()' under 4.0? How do I go about using it? (The manual page is particularly unclear).
wheatley@cme.nist.gov (Tom Wheatley) (09/22/89)
Yes, the man pages are somewhat useless for the mmap() call. The manual on WRITING DEVICE DRIVERS (for people who actually read them) is fortunately very clear. We tackled this problem back in early June and have had no problems whatsoever since then. The major changes are below. Each program is standalone for simple testing purposes. I do not profess to be the most elegant code writer, just a basic hack. Still, the code works. The old 3.5 version was : #include <sys/mman.h> /* used for mmap call */ #include <sys/types.h> /* used for mmap call */ #include <sys/file.h> /* used for open call */ main() { int vme16index; /* index from 0 in VME16D16 space */ int junk; /* placeholder */ int readjunk; /* placeholder */ int memfd; /* file descriptor of VME16D16 space */ char * v16base; /* base address from mmap call, the char causes all */ /* addresses to be interpreted as bytes. */ int i, ps, errno; /*======== Assign physical device to virtual memory address ====*/ memfd = open ("/dev/vme16d16", O_RDWR); if (memfd <0){ perror("/dev/vme16d16"); _exit(); } ps= getpagesize(); v16base = (char*) valloc(ps); if (v16base == 0){ perror("valloc"); _exit(); } printf("page size is %x\n", ps); printf("The Bit-3 is strapped for vme16d16 between 0 and 2000.\n"); printf("We get ps bytes starting from 0.\n"); errno = mmap(v16base,ps,PROT_READ|PROT_WRITE,MAP_SHARED,memfd,0x0); if (errno) { perror("mmap"); _exit(); } /* ========= try to read board=========== =================*/ for (i = 0; i < 10; i ++) { printf("Enter desired index between 0 and 2000.\n"); scanf("%x", &vme16index); printf("trying to read location %x\n", vme16index); junk = *(v16base + vme16index); /* attempt a read */ printf("junk read is %x\n", junk ); printf("Enter new data for write attempt.\n"); scanf("%x", &junk); *(v16base + vme16index) = junk; /* attempt a write */ readjunk = *(v16base + vme16index); /* read it back */ printf("read it back, junk wrote is %x\n", readjunk ); } close(memfd); /* clean-up and return to caller */ } If you want to use vme24 address space, just replace all vme16's with vme24's and v16base with v24base. The major changes under 4.0 were a lot of bells and whistles, most of which don't work, are not implemented, or just don't make sense in my opinion. The MAP_SHARED is the only flag I was able to get working. The MAP_FIXED, although it sounds like what most people would like, is discouraged according to the man pages. This is enforced by the refusal of the mmap call to work under MAP_FIXED. A working test program for version 4.0.3 is as follows: #include <stdio.h> #include <sys/mman.h> /* used for mmap call */ #include <sys/types.h> /* used for mmap call */ #include <sys/file.h> /* used for open call */ main() { int vmeoff; /* actual address of VME24D16 space */ int vme24index; /* index from above offset */ int junk[2048]; /* placeholder */ int readjunk[2048]; /* placeholder */ int memfd; /* file descriptor of VME24D16 space */ int *v24base; /* base address from mmap call */ int ps; int i,j; /*======== Assign physical device to virtual memory address ====*/ memfd = open ("/dev/vme24d16", O_RDWR); if (memfd <0){ perror("/dev/vme24d16"); _exit(); } ps= getpagesize(); printf("page size is %x\n", ps); /* We start at the offset vmeoff, continuing for ps bytes. Both must be */ /* multiples of the page size. */ printf("vmeoff must be a multiple of the page size.\n"); printf("Use vmeoff to get to multiples like B00000.\n"); printf("Use index of 100 to get to internal locations like B00100.\n"); printf("Enter vmeoff in HEX. We get ps bytes starting from there.\n"); scanf("%x", &vmeoff); v24base = mmap((caddr_t)0,ps,PROT_READ|PROT_WRITE,MAP_SHARED,memfd,vmeoff); printf("v24base is %d\n", v24base ); printf("v24base is %x\n", v24base ); if (v24base == (caddr_t)-1) { perror("mmap"); _exit(); } /* ========= try to read board=========== =================*/ for (i = 0; i < 2047; i ++) { readjunk[i] = i; junk[i] = i; } printf("Enter desired index from above vmeoff not to exceed ps.\n"); scanf("%x", &vme24index); printf("trying to read location %x\n", vmeoff + vme24index); for (j = 0; j < 100000; j ++) { for (i = 0; i < 2047; i ++) { *(v24base + vme24index + i) = junk[i]; /* attempt a write*/ } for (i = 0; i < 2047; i ++) { readjunk[i] = *(v24base + vme24index + i); /* attempt a read */ } } close(memfd); /* clean-up and return to caller */ } The major things here are the non-use of the valloc call, and the (very) different format of the mmap call and subsequent test for error. This was found in the WRITING DEVICE DRIVERS, and was quite clear. I just wish they had put in the the man pages. Actually, since the addr under 4.0 is created by the mmap call, it is clearer to me than under 3.5, where the addr was magically changed invisible to the user. In any case, have fun, and write back or call if you have any problems. Tom Wheatley (301)-975-3449
collberg@dna.lth.se (Christian S. Collberg) (03/24/90)
I am trying to do memory mapped IO in a 407 executable (the old executable format) and am getting strange results, namely the address returned by the mmap function being invalid. When I run the same program as a 413 executable everything works fine. I use the options PROT_WRITE, PROT_READ, and MAP_SHARED. I am running Sun's OS 4.0.3c on a SUN 3/80. Am I missing something obvious or am I just doing something very wrong? Thanks, Chris Collberg collberg@dna.lth.se