PS/2 device driver docs

A. Lester Buck buck at siswat.UUCP
Sun Jun 3 08:49:03 AEST 1990


In article <1517 at dutrun.UUCP>, vlrugvg at dutrun.UUCP (Ge van Geldorp) writes:
> I'm trying to write a device driver for AIX PS/2 1.1 and find the IBM
> documentation (Appendix C of the Technical Reference) less than clear.
> Sometimes the documentation even seems to lie:

>  - It says to compile the driver with the command `cc -c -DKERNEL -Di386
>    driver.c'. You better add a `-I/usr/include/sys' to this.

Yes, but this hardly takes much time to notice.

>  - The discussion of `ddopen' says the `oflag' argument of the open(2) system
>    call is passed as the `flag' argument to `ddopen'. Most of the bits
>    get passed unmodified, however, the O_RDONLY flag has the value 0
>    when passed to open(2), but `ddopen' receives a value of 1. O_WRONLY
>    and O_RDWR also exhibit this behaviour.

The kernel adds one to flag before passing it to the driver.  This gives
independent READ and WRITE bits, easier to test, but different from a stock
driver interface.  And, alas, not documented.

>  - subyte() is documented to return 0 if the address at which the byte
>    is to be stored is valid, -1 otherwise. It seems that subyte()
>    actually returns the byte which it stored (i.e. the `c' parameter).

Haven't experienced this one...

The one I lost most time over was the backwards documentation for
"well-behaved" ioctl's.  (They weren't.)  The examples

#define	TIOCGETD  _IOW(t, 0, int)	/* get line discipline */
...etc...

are backwards.  I.e., _IOR gets, _IOW puts.  The correct definitions are in
the ioctl header file.  (This was for V1.1, but I think I checked the new
V1.2 manuals available now and the same documentation error was there.)
Since there are no examples of actually using these well-behaved ioctl's, I
was shooting blind as to why it wasn't working.  The idea is very nice,
though, with the kernel doing all necessary copying before the driver
ioctl() routine is even called, and putting results into user space after
the ioctl() routine finishes.  There are some ioctl's which do not fit into
this scheme.  For example, Lachman Associates (now part of ISC) distributed
a set of standard ioctl's for Ethernet drivers talking with their TCP/IP
stack.  Several ioctl's were of a form which passed a count N and returned
the first N bytes of an internal structure, such as the multicast table.
This is not possible to encode in the "well-behaved" ioctl scheme of AIX
(and BSD and SunOS and ...).  But then this Lachman scheme is not a 
well designed ioctl interface.

One trick is how to do an ioctl that returns a value directly, such
as IOCTYPE, that every AIX driver should (theoretically) support.
Here you write the return value into part of the u structure and it
is passed back to the caller, unless u.u_error is also set.

> I looked around /etc/master to find an unused major number. My system
> had major values in the range 0-49, so I picked 50 as the major number
> of my driver. This resulted in lots of `no such device or address'
> messages. In dispair, I tried 19 as my major number (19 was not used in
> the /etc/master file). Presto, end of problem. Does this mean there is a
> hard-coded maximum major value of 49 somewhere? If so, is this
> documented anywhere?

A little history: At one point, AIX/RT 2.2.1 had a monster design defect
where they stored the file system type (AIX or DOS(!)) in the high bits
of the major number.  We were using a development machine that had
software loaded for about every adapter available on the RT and after adding
major number 32 (AIX merges block and character device switch tables),
all hell broke loose.  Turns out the kernel masks the 32 -> 0, and
guess which device is major 0?  The Disk...  (Where is that damn VRM maintenance
disk?)  IBM managed to hack around some and kludged things so that character
numbers could go to 256, but block numbers still had to be below 32.
And they whined that to fix this for real would require updating virtually
every module in the kernel...

The simple answer to your question is to not add major numbers by hand.  The
official way is to use the cfgadev() library routine, which "knows" how to
find free major numbers by reading /etc/master.  As far as a limit of 50, I
have never heard of that.  I assume you did mknod the special file for your
driver and the driver does a [CB]DEV_INSTALL to put it in the switch table
in its ddinit routine.  Was the major number in /etc/master when you built
the kernel?  (If you want to have fun on the RT, try filling up below
32 with character devices, then ask cfgadev() to add a block device.  It
would merrily hose your kernel without checking...  Note: the RT and
the PS/2 are not the same kernel code.)

> Suppose (well, don't suppose, I actually do this) I tell an adapter to
> do something, then go to sleep, to be waken up from an interrupt
> routine. Is there a race condition here? I.e. if the adapter immediately
> generates an interrupt before the user-context routine goes to sleep,
> will the wakeup() specifying a channel for which there is no correspon-
> ding sleep() yet be discarded or will the information be saved?

The standard method is to surround your code with an splN that blocks
interrupts at the device's level.  Something similar to:

	s = spl?();
	start_device_working();
	while( not_ready_to_awaken() ) {
	    sleep();
	}
	splx(s);

Presumably an interrupt routine or watchdog timer will finally post a wakeup
and set the condition that causes the while loop to be exited.  While the
process is sleeping and some other process is executing, the interrupt level
is at the level of that other process, whatever that might be.

> Could someone please provide an example of the use of the physio()
> routine? The 2-line explanation in the documentation is not sufficient
> for me to determine how to call this routine.

Here is the simplest example of using physio from an actual working driver:

void
mtread(dev)
dev_t	dev;
{
	physio (mtstrategy, (struct buf *)0, dev, B_READ);
}

void
mtwrite(dev)
dev_t	dev;
{
	physio (mtstrategy, (struct buf *)0, dev, B_WRITE);
}

mtstrategy keeps a queue of work to do and buffer addresses where to put the
data.  Physio allocates a buffer temporarily (but just uses the buffer
header), fills in the important stuff for a physical read/write, calls
mtstrategy, and does an iowait() inside physio until the buffer is freed by
your driver (with an iodone(bp)), then releases the buffer and returns.

If you want the man page on physio, look it up in the book "Writing a UNIX
Device Driver" or the AT&T Driver Development manuals.

-- 
A. Lester Buck     buck at siswat.lonestar.org  ...!texbell!moray!siswat!buck



More information about the Comp.unix.aix mailing list