[comp.unix.questions] 4.1 --> 4.3 BSD Device Driver Conversion

kutz@bgsuvax.UUCP (Kenneth Kutz) (05/12/88)

I will be attempting to port a 4.1 BSD device driver to run on our
4.3 BSD system.  The system is a VAX 11/785.  The device is a National
Instruments GPIB 11-2 (General Purpose Interface Board) for the
Unibus.  The interface supports DMA transfers and is IEEE-488 compliant.

If anyone has undergone a similar undertaking, I would appreciate any
pointers.  From looking at the errors from my compile listing, it
appears the return values are passed differently in the 4.1 implementation.

Thanks,


--------------------------------------------------------------------
      Kenneth J. Kutz         	CSNET kutz@bgsu.edu
				UUCP  ...!osu-cis!bgsuvax!kutz
--------------------------------------------------------------------
-- 
--------------------------------------------------------------------
      Kenneth J. Kutz         	CSNET kutz@bgsu.edu
				UUCP  ...!osu-cis!bgsuvax!kutz
 Disclaimer: Opinions expressed are my own and not of my employer's
--------------------------------------------------------------------

chris@mimsy.UUCP (Chris Torek) (05/13/88)

Here is something I just wrote this morning about it.

Converting drivers from 4.1BSD to 4.3BSD is usually fairly easy.
Most of the Unibus goo is unchanged.  The most noticeable
difference is that raw read and write functions went from

	fooread(dev)
		dev_t dev;
	{

		return (physio(foostrategy, &foobuf[minor(dev)], dev, B_READ,
			minphys));
	}

to

	fooread(dev, uio)
		dev_t dev;
		struct uio *uio;
	{

		return (physio(foostrategy, &foobuf[minor(dev)], dev,
			B_READ, minphys, uio));
	}

If the read and write functions deal directly with the device, rather
than going through physio, they must be changed to deal with `uio'
rather than global u.u_xxx variables.

Similarly, all the error handling in the open and close routines
is simplified.  Where in 4.1BSD you have

	fooopen(dev, flag)
		dev_t dev;
		int flag;
	{
		...
		if (something) {
			u.u_error = EBUSY;
			return;
		}
		...
	}

you change this to

		if (something)
			return (EBUSY);
		...
		return (0);
	}

(The close routine should return 0 for success, but a number of
drivers do not, and no one checks as yet.)

Finally, ioctl routines tend to get shorter.  Where before you
would write

	fooioctl(dev, cmd, addr, flag)
		dev_t dev;
		int cmd;
		caddr_t addr;
		int flag;
	{

		switch (cmd) {

		case FIOCSETFOO:
			u.u_error = copyin(addr, (caddr_t)&kernelvar,
				sizeof(struct foodata));
			...
		case FIOCGETFOO:
			u.u_error = copyout((caddr_t)&kernelvar, addr,
				sizeof(struct foodata));
			...
		default:
			u.u_error = ENOTTY;
		}
	}

this becomes

	fooioctl(dev, cmd, data, flag)
		dev_t dev;
		int cmd;
		caddr_t addr;
		int flag;
	{

		switch (cmd) {

		case FIOCSETFOO:
			kernelvar = *(struct foodata *)data;
			...
		case FIOCGETFOO:
			*(struct foodata *)data = kernelvar;
			...
		default:
			return (ENOTTY)
		}
		return (0);
	}

The copyin and copyout is all done at a higher level.  See
<sys/ioctl.h> for details; mainly, FIOCSETFOO might have been

	#define FIOCSETFOO _IO(f, 0)

so that it would become

	#define FIOCSETFOO _IOW(f, 0, struct foodata)

There is one limitation in the new ioctl mechanism: the size of
structures passed via ioctl is limited to <= 127 bytes.  If the
ioctl needs more data, you still need the copyin/copyout code,
but you would say `int error; ... error = copyin(...); if (error)'
rather than `u.u_error = copyin(...); if (u.u_error)'.

In general, 4.2BSD tried to purge driver references to `u.', so
that drivers could call other drivers.  The task is not quite
finished, but it is getting there.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris