[net.unix-wizards] Device Drivers in Xenix

nick@harvard.UUCP (Nick Vasilatos) (04/01/86)

> I am trying to write a device driver for SCO's Xenix SYSTEM V release 2

Assorted lore --

	A paddr_t is a (long) physical address (intended for real, live,
	on the bus addresses), see <sys/types.h>.

	The kernel, data first, is (or was, last I looked) loaded at 0x4000,
	so add 0x4000 to a kernel logical address to get a physical address.

	Far pointers are 32 bit pointers, the high word of which is an offset
	into either a particular local, or the system global descriptor table
	(LDT or GDT as it were; physical addresses are not directly meaningful
	to the 286 -- it operates on 16 bit address offsets interpreted
	relative to base addresses determined from the contents of it's
	various segment registers vis a vis these tables).

	To set up a far pointer for accessing physical addresses
	in a device driver:

		Call (unsigned) dscralloc() to obtain a gdt descriptor
		for exclusive use by your driver (a compile time configurable
		number of which are reserved for such use). It returns a
		``selector'' which is the (aforementioned) offset of the
		descriptor in the gdt.

		Fill in the elements (address, limit (segment size - 1)
		and protection) of the descriptor. Use:

			mmudescr((unsigned) selector, (paddr_t) address,
		 	 (unsigned) limit, (char) (access = DSA_DATA, from 
		 	 <sys/mmu.h>)); 

		The limit should be the size of the object you are trying 
		access. The hardware will favour you with a general
		protection trap if you exceed that limit (ie. miss) if you
		set it correctly.

		Make a far pointer out of the selector and a zero offset
		with a macro provided in <sys/param.h>:

			char far *dp = sotofar(selector,0);

	In as much as the 286 has a ruthlessly efficient string move,
	you probably don't want to code loops around far pointer
	assignments. For such as that use:

		copyseg((paddr_t) source, (paddr_t) dest, (unsigned) cnt);

	They are otherwise the right way to poke around the address space.
	If the device you are trying to drive's privies are memory
	mapped, you are in business. If they are I/O mapped (attached to
	the 286's seperate, 64k i/o bus) you are stuck with in and
	out instructions (accessed via in() and out() calls and i/o space
	relative 16 bit offsets).

	The drill in general is to create a C structure describing your
	device registers, data buffers, or what have you that you're trying
	to access and use it's size to determine the segment limit and 
	reference it to determine offsets, however you must apply them.

	The initialization handler of your driver is the apropo
	place to undertake descriptor allocation and setup.

	Physio disallows i/o into text or unallocated user memory
	(reasonable) -- a buffer that is mapped into a single data
	segment must be referenced by base and count in u. It also
	disallows odd addresses and counts. It also insists on even
	block multiples unless the BTAPE flag in the buffer header
	you're passing it is set.

nick@harvard.UUCP (Nick Vasilatos) (04/02/86)

>> I am trying to write a device driver for SCO's Xenix SYSTEM V release 2

> Assorted lore -- ammended

	Copyseg in reality takes far pointers, correct invocation would be
	more along the lines of:

 		copyseg((faddr_t) source, (faddr_t) dest, (unsigned) cnt);

	where a faddr_t is a char far * (typedef in <sys/types.h>).