All rights reserved.
%sccs.include.redist.roff%
@(#)6.t 6.3 (Berkeley) 04/17/91
.ds RH "Adding New Devices
ADDING NEW SYSTEM SOFTWARE
This section is not for the novice, it describes some of the inner workings of the configuration process as well as the pertinent parts of the system autoconfiguration process. It is intended to give those people who intend to install new device drivers and/or other system facilities sufficient information to do so in the manner which will allow others to easily share the changes.
This section is broken into four parts:
If you wish to make site-specific modifications to the system it is best to bracket them with #ifdef SITENAME ... #endif to allow your source to be easily distributed to others, and also to simplify diff\|(1) listings. If you choose not to use a source code control system (e.g. SCCS, RCS), and perhaps even if you do, it is recommended that you save the old code with something of the form: #ifndef SITENAME ... #endif We try to isolate our site-dependent code in individual files which may be configured with pseudo-device specifications.
Indicate machine-specific code with ``#ifdef vax'' (or other machine, as appropriate). 4.2BSD underwent extensive work to make it extremely portable to machines with similar architectures- you may someday find yourself trying to use a single copy of the source code on multiple machines.
Use lint periodically if you make changes to the system. The 4.3BSD kernel has only two lines of lint in it. It is very simple to lint the kernel. Use the LINT configuration file, designed to pull in as much of the kernel source code as possible, in the following manner. $ cd /sys/conf $ mkdir ../LINT $ config LINT $ cd ../LINT $ make depend $ make assym.s $ make -k lint > linterrs 2>&1 & (or for users of csh\|(1)) % make -k >& linterrs This takes about an hour on a lightly loaded VAX-11/750, but is well worth it. Adding non-standard system facilities
This section considers the work needed to augment config 's data base files for non-standard system facilities. Config uses a set of files that list the source modules that may be required when building a system. The data bases are taken from the directory in which config is run, normally /sys/conf. Three such files may be used: files , files .machine, and files .ident. The first is common to all systems, the second contains files unique to a single machine type, and the third is an optional list of modules for use on a specific machine. This last file may override specifications in the first two. The format of the files file has grown somewhat complex over time. Entries are normally of the form
dir/source.c type option-list modifiersfor example,
vaxuba/foo.c optional foo device-driverThe type is one of standard or optional . Files marked as standard are included in all system configurations. Optional file specifications include a list of one or more system options that together require the inclusion of this module. The options in the list may be either names of devices that may be in the configuration file, or the names of system options that may be defined. An optional file may be listed multiple times with different options; if all of the options for any of the entries are satisfied, the module is included.
If a file is specified as a device-driver , any special compilation options for device drivers will be invoked. On the VAX this results in the use of the -i option for the C optimizer. This is required when pointer references are made to memory locations in the VAX I/O address space.
Two other optional keywords modify the usage of the file. Config understands that certain files are used especially for kernel profiling. These files are indicated in the files files with a profiling-routine keyword. For example, the current profiling subroutines are sequestered off in a separate file with the following entry:
sys/subr_mcount.c optional profiling-routineThe profiling-routine keyword forces config not to compile the source file with the -pg option.
The second keyword which can be of use is the config-dependent keyword. This causes config to compile the indicated module with the global configuration parameters. This allows certain modules, such as machdep.c to size system data structures based on the maximum number of users configured for the system. Adding device drivers to 4.3BSD
The I/O system and config have been designed to easily allow new device support to be added. The system source directories are organized as follows:
/sys/h machine independent include files |
/sys/sys machine-independent system source files |
/sys/conf site configuration files and basic templates |
/sys/net network-protocol-independent, but network-related code |
/sys/netinet DARPA Internet code |
/sys/netimp IMP support code |
/sys/netns Xerox NS code |
/sys/vax VAX-specific mainline code |
/sys/vaxif VAX network interface code |
/sys/vaxmba VAX MASSBUS device drivers and related code |
/sys/vaxuba VAX UNIBUS device drivers and related code |
Existing block and character device drivers for the VAX reside in ``/sys/vax'', ``/sys/vaxmba'', and ``/sys/vaxuba''. Network interface drivers reside in ``/sys/vaxif''. Any new device drivers should be placed in the appropriate source code directory and named so as not to conflict with existing devices. Normally, definitions for things like device registers are placed in a separate file in the same directory. For example, the ``dh'' device driver is named ``dh.c'' and its associated include file is named ``dhreg.h''.
Once the source for the device driver has been placed in a directory, the file ``/sys/conf/files.machine'', and possibly ``/sys/conf/devices.machine'' should be modified. The files files in the conf directory contain a line for each C source or binary-only file in the system. Those files which are machine independent are located in ``/sys/conf/files,'' while machine specific files are in ``/sys/conf/files.machine.'' The ``devices.machine'' file is used to map device names to major block device numbers. If the device driver being added provides support for a new disk you will want to modify this file (the format is obvious).
In addition to including the driver in the files file, it must also be added to the device configuration tables. These are located in ``/sys/vax/conf.c'', or similar for machines other than the VAX. If you don't understand what to add to this file, you should study an entry for an existing driver. Remember that the position in the device table specifies the major device number. The block major number is needed in the ``devices.machine'' file if the device is a disk.
With the configuration information in place, your configuration file appropriately modified, and a system reconfigured and rebooted you should incorporate the shell commands needed to install the special files in the file system to the file ``/dev/MAKEDEV'' or ``/dev/MAKEDEV.local''. This is discussed in the document ``Installing and Operating 4.3BSD on the VAX''. Autoconfiguration on the VAX
4.3BSD requires all device drivers to conform to a set of rules which allow the system to:
In addition, devices such as the RK07 which require everyone else to get off the UNIBUS when they are running need cooperation from other DMA devices if they are to work. Since it is unlikely that you will be writing a device driver for a MASSBUS device, this section is devoted exclusively to describing the I/O system and autoconfiguration process as it applies to UNIBUS devices.
Each UNIBUS on a VAX has a set of resources:
There is a structure of type struct uba_hd in the system per UNIBUS adapter used to manage these resources. This structure also contains a linked list where devices waiting for resources to complete DMA UNIBUS activity have requests waiting.
There are three central structures in the writing of drivers for UNIBUS controllers; devices which do not do DMA I/O can often use only two of these structures. The structures are struct uba_ctlr, the UNIBUS controller structure, struct uba_device the UNIBUS device structure, and struct uba_driver, the UNIBUS driver structure. The uba_ctlr and uba_device structures are in one-to-one correspondence with the definitions of controllers and devices in the system configuration. Each driver has a struct uba_driver structure specifying an internal interface to the rest of the system.
Thus a specification controller sc0 at uba0 csr 0176700 vector upintr would cause a struct uba_ctlr to be declared and initialized in the file ioconf.c for the system configured from this description. Similarly specifying disk up0 at sc0 drive 0 would declare a related uba_device in the same file. The up.c driver which implements this driver specifies in its declarations: int upprobe(), upslave(), upattach(), updgo(), upintr(); struct uba_ctlr *upminfo[NSC]; struct uba_device *updinfo[NUP]; u_short upstd[] = { 0776700, 0774400, 0776300, 0 }; struct uba_driver scdriver = { upprobe, upslave, upattach, updgo, upstd, "up", updinfo, "sc", upminfo }; initializing the uba_driver structure. The driver will support some number of controllers named sc0, sc1, etc, and some number of drives named up0, up1, etc. where the drives may be on any of the controllers (that is there is a single linear name space for devices, separate from the controllers.)
We now explain the fields in the various structures. It may help to look at a copy of vaxuba/ubareg.h, vaxuba/ubavar.h and drivers such as up.c and dz.c while reading the descriptions of the various structure fields.
uba_driver structureOne of these structures exists per driver. It is initialized in the driver and contains functions used by the configuration program and by the UNIBUS resource routines. The fields of the structure are:
The autoconfiguration routine attempts to verify that the specified address responds before calling the probe routine. However, the device may not actually exist or may be of a different type, and therefore the probe routine should use delays (via the DELAY(n) macro which delays for n microseconds) rather than waiting for specific events to occur. The routine must not declare its argument as a register parameter, but must declare register int br, cvec; as local variables. At boot time the system takes special measures that these variables are ``value-result'' parameters. The br is the IPL of the device when it interrupts, and the cvec is the interrupt vector address on the UNIBUS. These registers are actually filled in in the interrupt handler when an interrupt occurs.
As an example, here is the up.c probe routine: upprobe(reg) caddr_t reg; { register int br, cvec; #ifdef lint br = 0; cvec = br; br = cvec; upintr(0); #endif ((struct updevice *)reg)->upcs1 = UP_IE|UP_RDY; DELAY(10); ((struct updevice *)reg)->upcs1 = 0; return (sizeof (struct updevice)); } The definitions for lint serve to indicate to it that the br and cvec variables are value-result. The call to the interrupt routine satisfies lint that the interrupt handler is used. The cod here enable interrupts on the device and write the ready bit UP_RDY. The 10 microsecond delay insures that the interrupt enable will not be canceled before the interrupt can be posted. The return of ``sizeof (struct updevice)'' here indicates that the probe routine is satisfied that the device is present (the value returned is not currently used, but future plans dictate that you should return the amount of space in the device's register bank). A probe routine may use the function ``badaddr'' to see if certain other addresses are accessible on the UNIBUS (without generating a machine check), or look at the contents of locations where certain registers should be. If the registers contents are not acceptable or the addresses don't respond, the probe routine can return 0 and the device will not be considered to be there. One other thing to note is that the action of different VAXen when illegal addresses are accessed on the UNIBUS may differ. Some of the machines may generate machine checks and some may cause UNIBUS errors. Such considerations are handled by the configuration program and the driver writer need not be concerned with them. It is also possible to write a very simple probe routine for a one-of-a-kind device if probing is difficult or impossible. Such a routine would include statements of the form: br = 0x15; cvec = 0200; for instance, to declare that the device ran at UNIBUS br5 and interrupted through vector 0200 on the UNIBUS.This is the routine which is called by the UNIBUS resource management routines when an operation is ready to be started (because the required resources have been allocated). The routine in up.c is: updgo(um) struct uba_ctlr *um; { register struct updevice *upaddr = (struct updevice *)um->um_addr; upaddr->upba = um->um_ubinfo; upaddr->upcs1 = um->um_cmd|((um->um_ubinfo>>8)&0x300); } This routine uses the field um_ubinfo of the uba_ctlr structure which is where the UNIBUS routines store the UNIBUS map allocation information. In particular, the low 18 bits of this word give the UNIBUS address assigned to the transfer. The assignment to upba in the go routine places the low 16 bits of the UNIBUS address in the disk UNIBUS address register. The next assignment places the disk operation command and the extended (high 2) address bits in the device control-status register, starting the I/O operation. The field um_cmd was initialized with the command to be stuffed here in the driver code itself before the call to the ubago routine which eventually resulted in the call to updgo.
One of these structures exists per-controller. The fields link the controller to its UNIBUS adapter and contain the state information about the devices on the controller. The fields are:
A back-pointer to the UNIBUS adapter to which this controller is attached.
One of these structures exist for each device attached to a UNIBUS controller. Devices which are not attached to controllers or which perform no buffered data path DMA I/O may have only a device structure. Thus dz and dh devices have only uba_device structures. The fields are:
The number of the controller on which this device is attached, or -1 if this device is not on a controller.
The interrupt vector entries for this device, copied into the UNIBUS interrupt vector at boot time. The values of these fields are filled in by config to small code segments which it generates in the file ubglue.s.
The iostat number assigned to this device. Numbers are assigned to disks only, and are small nonnegative integers which index the various dk_* arrays in <sys/dk.h>.
A struct uba_ctlr pointer to the controller (if any) on which this device resides.
A struct uba_hd pointer to the UNIBUS on which this device resides.
UNIBUS resource management routinesUNIBUS drivers are supported by a collection of utility routines which manage UNIBUS resources. If a driver attempts to bypass the UNIBUS routines, other drivers may not operate properly. The major routines are: uballoc to allocate UNIBUS resources, ubarelse to release previously allocated resources, and ubago to initiate DMA. When allocating UNIBUS resources you may request that you
If the presentation here does not answer all the questions you may have, consult the file /sys/vaxuba/uba.c
Autoconfiguration requirementsBasically all you have to do is write a ud_probe and a ud_attach routine for the controller. It suffices to have a ud_probe routine which just initializes br and cvec, and a ud_attach routine which does nothing. Making the device fully configurable requires, of course, more work, but is worth it if you expect the device to be in common usage and want to share it with others.
If you managed to create all the needed hooks, then make sure you include the necessary header files; the ones included by vaxuba/ct.c are nearly minimal. Order is important here, don't be surprised at undefined structure complaints if you order the includes incorrectly. Finally, if you get the device configured in, you can try bootstrapping and see if configuration messages print out about your device. It is a good idea to have some messages in the probe routine so that you can see that it is being called and what is going on. If it is not called, then you probably have the control-status register address wrong in the system configuration. The autoconfigure code notices that the device doesn't exist in this case, and the probe will never be called.
Assuming that your probe routine works and you manage to generate an interrupt, then you are basically back to where you would have been under older versions of UNIX. Just be sure to use the ui_ctlr field of the uba_device structures to address the device; compiling in funny constants will make your driver only work on the CPU type you have (780, 750, or 730).
Other bad things that might happen while you are setting up the configuration stuff: