This directory contains a number of network, disk and other
drivers.  Attached at the end of this document is the original README.

	Believe it or not, you won't have to really understand how a driver
works in order to install it.  Just follow the bouncing ball ...  Note that
some of the old `glue' used to patch drivers in this directory can be found
in the "GLUE" subdirectory.

STEP #1:
	Install the source code in the proper directory.  A pdp MASSBUS
driver would go in pdpmba, a pdp UNIBUS driver would go in pdpuba.  In
reality (the present system) all drivers are in pdpuba regardless of
being MASSBUS or UNIBUS.  We'll use the versatec driver as an example.  
Since it's a UNIBUS driver, we'll move vp.c into pdpuba.  If you're installing 
a driver from the OTHERS directory, or you've got a local work of art that 
you wish to use, there are a few things you should check so that it will 
work correctly with 2.11BSD:

	Note, in the following examples, "XX" is the two or three letter
	designation that device drivers under UNIX get assigned, e.g. "vp"
	for the Versatec driver, "vv" for the Pronet driver, etc.

	a) The XXattach routines should return 1 on success, 0 on failure.

	b) If your XXread and XXwrite routines use physio(), make sure that
	   you're passing the correct number and types of arguments.

	   Between the initial release of 2.11BSD and the present the 
	   WORD/BYTE argument (added between 2.9 and 2.10) was removed.
	   If it is important to enforce alignment this can be done in
	   either the strategy routine or in XXread/XXwrite before calling

	c) The XXread and XXwrite routines should return 0, if successful,
	   and the correct errno value (for later assignment to u.u_error)
	   if they fail.  All the XXread and XXwrite routines which only
	   called physio() have been removed (the common routines rawread()
	   and rawwrite() replacing XXread() and XXwrite() in the cdevsw[]

	   Note:  If XXread() and/or XXwrite() only calls physio() then
	   the XXread and XXwrite entry points can be removed and the common
	   routines 'rawread', 'rawwrite' used in the cdevsw[] table.  See
	   the entries for any of the disc or tape drivers for examples of

	d) The XXioctl routine has changed a *lot* between 2.9 and 2.11:

		+ XXioctl() now takes four arguments, (dev, cmd, data,
		  flags) a device (dev_t), the ioctl command (u_int), a
		  buffer address (caddr_t), and some flags (int).  Of real
		  interest is the command, which is the low order 16 bits
		  of the ioctl command.  Ioctl's in user land are 32 bits.
		  The low order word contains the ioctl command itself, the
		  high order describes any necessary argument transfers
		  between the kernel and user process.  See sys/ioctl.h for
		  macros IO, IOR, IOW and IORW that implement this.
		  Sys/mtio.h is a good file to look at for how to add the
		  correct definitions to your include file.  THIS IS THE
		  TRICKY PART!!  Look at how other device drivers handle this.

		+ XXioctl() no longer copies anything in or out of user
		  land.  The new upper word of the ioctl "cmd" allows the
		  ioctl system call to perform all necessary transfers
		  between the kernel and the user process.  See the kernel
		  ioctl() routine for more info.

		+ XXioctl has to return 0 on success, or the correct errno
		  value for later assignment to u.u_error.

	e) Make sure that you handle buffer allocation correctly.  If you
	   wish to be portable, there should be allocations of mapping
	   registers using mapalloc() for UNIBUS mapped machines, ifdef'd
	   on "UNIBUS_MAP".  You should ifdef *real* 22-bit QBUS addressing
	   with the define "Q22" (see the comments in conf/GENERIC and
	   pdp/machdep2.c.)  Buffer cache buffers have to be mapped in to
	   be accessed in 2.9BSD and 2.11BSD.  I don't really understand
	   how it worked before then, but some of the drivers in the OTHERS
	   directory predate that, so be paranoid.

	f) The device addresses and partition table sizes should be
	   included in the device driver itself, not in ioconf.c (which
	   is used for an entirely different purpose now.)  See the drivers 
	   in pdpuba for examples.

	g) You should have an XXopen() routine that validates the unit
	   number for the read, write, and ioctl system calls.  See the
	   the drivers in pdpuba for examples.  Normally they will look
	   something like:
			dev_t	dev;
			if (minor(dev) > XXRL || !XXADDR)
				return (ENXIO);
			return (0);

	   If you've already got the driver lying around, the XXstrategy
	   routine probably already does this.  The read/write/ioctl
	   routines *MAY* do it, i.e. you can rip it out of them once
	   you've got the open routine working.  Note, make sure that
	   you don't assign an absolute address to XXADDR when you declare
	   it (a lot of old drivers do) as it negates the test of
	   XXADDR in XXopen().  Also, a lot of old drivers use the minor
	   numbers in strange and wonderful ways; don't assume that the
	   above test, "minor(dev) > XXRL", is correct for your device.
	   Anyway, this is a Very Important Thing.  If you don't do this
	   fix right, interesting things will happen when someone accesses
	   a device for which you have a node in /dev, but which doesn't
	   really exist.

STEP #2:
	Write some code that will attempt to "probe" the routine.  See any
of the other device drivers for examples.  Install this code in the
"sys/autoconfig" directory, only call it "XXauto.c", where XX is
the prefix for the driver.  In our example, we'll install it as
"sys/autoconfig/vpauto.c".  This is the code that is used by the auto 
configuration program to get to the device.  By convention, this routine 
must be named "XXprobe()".  It's also useful to check and see if any of 
the probe routines already written would be identical to your probe routine.
For example, the "xpauto.c" file contains the probe routine for several 
flavors of disk drive.  If you're porting a 4BSD driver, it probably 
already has a probe routine in it which may be close to what you want.  
Also, in any case, rip the XXprobe stuff out of the main file ... it is never 
used by the kernel and it just wastes text space.
	Previously the XXauto.c files were kept in the same directory as
the device drivers themselves.  This lead to the misleading assumption that 
'/etc/autoconfig' would automatically be rebuilt if the probe routine in
the XXauto.c files in sys/pdpuba were modified.  To clarify matters the 
XXauto.c files were moved into the sys/autoconfig directory.

STEP #3:
	Go to the auto configuration directory, "sys/autoconfig" and update
the "DOBJS" and "SRCS" defines to reflect the existence of the new device
driver.  Our example would add "vpauto.c" to the "SRCS" define, and
"vpauto.o" to the "DOBJS" define.  Update the source file
"sys/autoconfig/uprobe.c" to reflect the existence of the new device
driver.  Basically, you have to define the "vpprobe" routine and then add a
new line to the "uprobe" table.  For our example, we'd add:

	int	vpprobe();

at the beginning of the file and:

	"vp",	vpprobe,	/* vp -- Versatec */

in the "uprobe" table.

STEP #4:
	Update the "/etc/dtab" table to reflect the new drive.  You may be
able to simply uncomment an existing line, otherwise, figure out the
correct values for each field and write a new one.  Our example needs an
entry of the format:

	# vp	? 177500 174	4	vpintr		# Versatec

Where "vp" is the letter designation, "?" means that we aren't assigning a
specific unit number (the normal case), 177500 is the address of the
device, 174 is the interrupt vector, 4 is the interrupt priority level
(Br), and "vpintr" is the handler(s) as designated in the file "scb.s".

STEP #5:
	In scb.s you'll need to add an interrupt interface:

	a) Add an include of "XX.h" to the top so that the presence or
	   absence of the driver can be determined.

	b) If the driver is for a disk you'll need to add an interrupt
	   vector definition so that device interrupts can be caught.  In
	   general this won't have to be done for most devices because
	   /etc/autoconfig will assign, initialize and probe the device
	   after the system has booted.  Disks have to have their interrupt
	   vectors already set up at boot time for obvious reasons.  For
	   convenience several of the networking drivers are also hardwired
	   in, but that probably wasn't necessary.  The Versatec, for instance,
	   does not need an interrupt vector set up at boot time, so its
	   vector will be set up by /etc/autoconfig.

	   If an interrupt vector is needed, it can be added by determining
	   the device's interrupt vector address, IADDR, and then adding
	   lines of the form:

		#if NXX > 0
			DEVTRAP(IADDR, XXintr, brX)

	   numerically in order with the other sets of lines like the above
	   already present in scb.s.  "IADDR", as mentioned above, would be
	   the devices interrupt vector address, "XXintr" is the name of
	   the interrupt handling routine in the C source of your driver,
	   and "brX" is the priority level that the device interrupts at.
	   Note that "XXintr" is the handler name that would be added to
	   /etc/dtab.  Also note that some devices have more than one
	   interrupt vector that must be set up.

	   If (and it doesn't!) the versatec needed an interrupt vector
	   assigned at boot time, it would look like the following:

		#if NVP > 0
			DEVTRAP(174, vpintr, br4)

	c) Finally, add an interrupt interface thunk in the last half of
	   scb.s to correspond with the new device's interrupt vector(s).
	   These small thunks catch the device's interrupts and hand
	   control off to the the real handler routine after first saving
	   the machine's current context by (essentially) passing the
	   address of the real handler to the routine "call" in pdp/mch_trap.s.

	   The following would be added for the Versatec driver:

		#if NVP > 0

STEP #6:
	Update the conf.c file (it used to be called c.c, in previous
incarnations).  This file needs to have an include line for the .h file
that will determine if this driver gets included in the kernel, a set of
defines so that the kernel can compile correctly whether you not you choose
to include the driver, and entries in the block and/or character device
switch tables.  For our example we would add:

#include "vp.h"
#if NVP > 0
int	vpopen(), vpclose(), vpwrite(), vpioctl();
#define	vpstrategy	nulldev
#define	vpopen		nodev
#define	vpclose		nodev
#define	vpwrite		nodev
#define	vpioctl		nodev
#define	vpstrategy	nodev
#endif	NVP

to the character device definition section of conf.c, and

/* vp = 20 */
	vpopen,		vpclose,	nodev,		vpwrite,
	vpioctl,	nulldev,	0,		SELECT(nodev),

to the character device switch table (cdevsw[]).

The strategy member of the cdevsw[] structure is new since 2.11BSD was 
released.  Since VP does not have a strategy routine a dummy definition 
is created.  Other devices (such as disc and tape drivers) do have
strategy routines and their entries look like:

#include "tms.h"
#if NTMS > 0
int	tmscpopen(), tmscpclose(), tmscpstrategy(), tmscpioctl();
#define	tmscpopen	nodev
#define	tmscpclose	nodev
#define	tmscpioctl	nodev
#define	tmscpstrategy	nodev

/* tmscp = 23 (tu81/tk50) */
	tmscpopen,	tmscpclose,	rawread,	rawwrite,
	tmscpioctl,	nulldev,	0,		seltrue,

	Note, in our example, we've been assuming that NVP is the define that
governs whether or not the device is included, as well as how many of them
we have.  For example, "#define NVP 5" in the file "vp.h" means that we
have 5 of the devices, and "#define NVP 0" means that we don't even include
the driver.  More on "vp.h" a little later.

	Since the versatec is a character device only, it only gets
included in the character device switch table, "cdevsw[]". (for a disk
driver, for instance, you would have entries in both the character and
block device switch tables) The entries in the table correspond to the
routines that get called when you open a versatec, close a versatec, etc.
etc.  See the include file "sys/conf.h" for more information on what the
fields in the block and character switches mean.  The easiest method to
make a new entry is to find a device that looks like yours and do what it

	Note, the "major" number that you'll use later when you add the node
to the "/dev" directory is the offset into this table, or, in the above
example, 20.

STEP #7:
	Next, update the following files: "conf/GENERIC", "conf/config",
"conf/Make.pdpuba" (or "conf/Make.pdpmba" or "conf/Make.net"), and
"conf/Makefile".  Basically, add lines of the form:

NVP		0		# Versatec
VP_TWOSCOMPL	NO		# interface needs two's complement bytecount

in "conf/GENERIC".  These are the lines that get changed when you want to
configure a new system.  Some drivers may only need a single line to
describe the number of devices to be configured in, others, like the
Versatec need auxiliary configuration information.  Change "conf/config"
to do the intelligent thing with those lines, i.e. something like

echo "#define NVP	$NVP"		> ../$MACHINE/vp.h
if [ $VP_TWOSCOMPL = YES ] ; then
	echo "#define VP_TWOSCOMPL"	>> ../$MACHINE/vp.h

which will place the appropriate defines for the Versatec into vp.h.
Obviously, the source code for your driver should modify its behavior based
on the defines found in its include file.

	Note that if you do have additional configuration options for your
driver they *should* be put into conf/GENERIC with appropriate comments.
Don't hide them in pdpuba/vpreg.h for instance - that's the way things used
to be done in 2.9BSD and earlier, making it extremely difficult to
configure a system.  With all of the relevant options in one place
(conf/GENERIC) it is very simple for the person configuring a new system
to just `check off' the items they want.

	Finally, add the resulting object file names for your device
driver to "conf/Makefile" and "conf/Make.pdpuba" (or "conf/Make.pdpmba"
or "conf/Make.net").  The former just needs to know the name so it can
load it; the latter must be able to compile the file.  Be careful where
you have "conf/Makefile" load your object as certain things have to be
loaded in special places; in general, just put your object name in an
overlay or the base segment.

STEP #8:
	You should also edit "dev/MAKEDEV.local" to make nodes with the
correct major/minor numbers in the /dev directory.  The major number for
character devices is the offset of the device's entry in cdevsw[], and
the major number for block devices is the similar offset in bdevsw[].

	One final note, we've had fair success with porting 4BSD drivers.
You just have to rip out all of the stuff that understands about multiple
UNIBUS's and you're halfway there.  Two things you have to check; it may
play pretty loose and casual with buffers since it doesn't cost anything to
have them.  It's EXPENSIVE on the PDP-11, because you have to do a mapin()
every time you want one, plus, you can only have one at a time!!  Look for
calls to geteblk(), it's a dead giveaway.  Also, MBUF allocation is
different in the two systems, if the driver goes for one of them, take a
long look at it.

	As far as the various device drivers in this directory, note that
comments like "will probably converted soon" and "should work without
modification" are probably now specious.  While comments like "is untested",
"might work with ...", etc. should be taken to mean "it didn't work on
2.9BSD and you'd probably be better off rewriting it from scratch or buying
an IBM PC" ...

	Several of the drivers in the OTHERS directory should eventually
be removed because working versions exist in the pdpuba directory.  These
are the ts-11 (OTHERS/ts - pdpuba/ts.c), rp04/rp06 (OTHERS/rp04.06 - 
pdpuba/xp.c), TMSCP (OTHERS/tk - pdpuba/tmscp.c), RX01&RX02 (OTHERS/rx01, 
OTHERS/rx02 - pdpuba/rx.c), MSCP (OTHERS/ra - pdpuba/ra.c), TU-16 (OTHERS/ht -
pdpuba/ht.c), rm02/rm03/rm05 (OTHERS/rm02.03.05 - pdpuba/xp.c), Fuji-160
(OTHERS/fuji_160 - pdpuba/xp.c), dm11 (OTHERS/dm11 - pdpuba/dh.c).

Keith Bostic & Casey Leedom (revised by Steven Schultz)

	The device drivers in this directory are here because they are
untested, or have not been completely converted to use new ioctl or
buffering protocols, or because they are of limited interest or use.  Some
of them have comments at the beginning describing their state.  If you test
and/or finish any of these drivers, please let the 2BSD project know about it.

	The dc and dj drivers don't have the current ioctl mechanism.  They
are untested.

	The dmc and du drivers don't know how to handle system buffers,
since there are no in-address-space buffers any longer.  They could
probably be made to work with dedicated buffers or mapped-in buffers.  The
bk driver (berknet line discipline) also has this problem, but will
probably be converted and tested soon.  It has never worked on PDP11's to
my knowledge.

	The rx2, rx3 and ml drivers have been modified only slightly from
working versions received from the authors.  They will probably work
without modification.

	The multiplexor driver has not been made to work with anything like
the current system.  It is unmodified (as far as I know); if you try it,
check the order and types of arguments to the external entry points.

	The cr (card reader) driver is probably flaky, at least with this