TOC PREV NEXT INDEX

Put your logo here!


PART I · DRIVER ARCHITECTURE

2 GENERAL DRIVER ARCHITECTURE

In general a driver software can be organized into four logically separated functional units.

Three of the functional units are software layers, forming a vertical hierarchy where a higher layer directly controls the next lower layer. The flow of control is strictly downwards while the flow of status and data is strictly upwards the hierarchy.

In parallel to the layer stack there exists a fourth component, the interrupt unit. This unit includes all interrupt related functions. It cannot be placed in the layer hierarchy because it supports the user interface as well as the device directly.

The purpose and functions of the layers are explained in more detail in the following sections.

2.1 The Device Layer

The device layer is the lowest layer in the hierarchy. It provides the direct hardware access, i.e. the routines of this layer are the only which directly read and write the device registers. Therefore, the device layer is the only part of the driver which is really hardware dependent.

The device layer routines map the device functionality nearly one to one. That means, for each device feature there should exist a device layer routine to use it. The routines are designed to perform just one elementary action on the device. The combination of such elementary actions in order to execute a user-command is performed on the middle layer. Also, no checks of parameter range, access rights etc. are made on this layer.

Note: Only one device layer routine is allowed to access a device at a time! Care must be taken on the higher layers to protect the executions of device accessing commands.

For details of the way to handle more than one device with the same driver see section 2.6.

2.2 The Command Layer

The Command Layer is the middle layer in the hierarchy. It contains routines to execute the functions which are invoked by ioctl commands. The routines of this layer use the device layer routines to start the required actions and get the requested status values.

The routines should be named xxx<Command> where <Command> describes the command purpose and is textual identical with the corresponding literal as defined in the xxxCommands.h header file.

Each command routine must return a status value which is passed backwards through the interface layer and the I/O system to the caller.

Note: The routines of this layer which call device layer routines must be executed under semaphore protection in order to inhibit simultaneous device access from multiple tasks!

For more details of the overall command handling see section 2.8.

2.3 The User Interface Layer

The Interface Layer forms the highest layer in the driver layer hierarchy. It provides the routines to install the driver and the devices as well as the routines to connect to the VxWorks I/O system.

Users communicate with the driver by using the VxWorks I/O system, i.e. opening channels to the driver with the standard open routine, invoking commands with the ioctl function and closing channels by calling close.1

The VxWorks I/O system processes such calls by calling the appropriate routines of the xxx driver which are called xxxOpen, xxxIoctl and xxxClose. These routines return a status code which is passed back to the user by the I/O system. An exception is the open call which returns the file handle if successful or just ERROR if any error occurred. In this case the error code is returned by the driver in the third call parameter of the open routine.

Following the VxWorks conventions the routine to install the driver is named xxxDrv, and the routine to add a device is named xxxDevCreate. These routines are called directly during start-up (e.g. from a script-file). It is not recommended to invoke these functions during run-time.

2.4 Interrupt Handlers

This section describes a general concept of handling interrupts form a device within the driver and within user supplied interrupt service routines (ISR).

The interrupt unit of a driver should include a central interrupt handler which is connected to all interrupts from the devices managed by the driver. This connection is established with the intConnect system call at driver or device installation time.

A user ISR can be connected to an interrupt by two ways:

· On interrupt level: In this case the user ISR is called directly from the central interrupt handler and runs with the constraints on interrupt level (see [3]).
· The user ISR runs on task level: The synchronization between the task and the central interrupt handler is done via a binary semaphore. In this case the user ISR waits for this semaphore which is given by the central interrupt handler when the interrupt occurs.
A variation of this concept is a dedicated task, which does the semaphore handling itself and calls the user ISRs as normal subroutines. This requires that the driver must provide this task.

2.5 Channel Handling

A user gains access to a device by invoking the open function which is usually called to open a `file'. The term `channel' is used instead in this document for such a connection because its purpose is to provide a channel of control between the user and the device.

The driver manages channels by use of an internal data structure called the Driver Channel Table (DCT). The DCT provides entries, called slots, which keep the pointer to the Device Descriptor to which the channel is connected, and the access rights (see section 3.3.5 and 4.1). The DCT is allocated during driver installation. The number of entries is a parameter to this call.

On an open call the driver looks for a free DCT slot. The index of this slot is returned to VxWorks as that value which shall be passed to all further calls to distinguish between different channels (see [3], "I/O System"). The size of the DCT limits the number of simultaneously open channels in the driver.

On an ioctl call the driver receives the DCT slot number from VxWorks and gets the device descriptor an the access rights from this entry.

In this way the DCT serves like the file descriptor for the user, but there is no need for the driver to know the file descriptor. The DCT index can be considered as a driver internal file descriptor.

2.6 Multi Device Handling

In general a driver has to be able to handle more than one device of the same type. Devices can be added to the driver with the xxxDevCreate call. Each device connected is called a device unit.

Each unit is specified by its Device Descriptor. The driver maintains these data structure in form of a table (array). The table is allocated in the xxxDrv routine, while entries to this table are initialized in xxxDevCreate. At the top of a Device Descriptor is the device header as required by VxWorks for the system device table. The header can be followed by device specific parameters. The values of these parameters are passed to or calculated by xxxDevCreate.

On an ioctl or close call the driver can reference the Device Descriptor to which the channel is connected through the entry in the Driver Channel Table. If routines in other layers need a reference to a specific device then the pointer to its Device Descriptor should be passed to that routine.

2.7 Multi Task Handling

This is closely related to `Multi Device Handling'. In fact a multi-device implementation might work fine as long as only one task is accessing the devices. But there might be hidden traps...

A driver has to protect all its devices as well as its global data against simultaneous access by multiple tasks. This should be done with mutex-semaphores, which VxWorks provides. [3]

A semaphore can be created either at driver-level in xxxDrv or at device-level in xxxDevCreate. The second solution is better, because then one task cannot block all other tasks trying to get access to any device of the driver, but only to the same device.

Sometimes devices are not completely independent of each other, for instance several devices can be located on the same hardware board and have some of its registers in common. If a device is supposed to be protected with a device-level semaphore, but shares some registers with other devices, then this will not work correctly in a multi-tasking environment! In this case a semaphore has to protect the whole board, not a single device. So all devices on the same board must share the same semaphore. If this is not possible for some reason then the last solution is a driver-level semaphore.

7 Make sure that ALL parts of a device are protected by a semaphore!

Another important issue is the handling of global data. If some code, which is supposed to be guarded by a device-level semaphore, makes use of global data, then this will also not work correctly. Other tasks may at the same time access other devices but the same global data, simply because they use another semaphore. Instead of implementing complicated constructions to solve this:

7 Avoid global variables for device handling!

If semaphores are not applicable for some reason then the VxWorks functions taskLock/taskUnlock can be used, but the amount of code running under task-lock should be kept to a minimum. Make sure that a function does not set the task-lock and then take some error-return without doing a task-unlock. There must be as many taskUnlock's as taskLock's!

7 One `taskLock' must always be followed by one `taskUnlock'!

2.8 Command Handling

This section describes the overall concept of handling ioctl commands in the driver.

It is recommended to use a Command Descriptor Table as the central entity of the command handling. This table is an array of structures, each entry describing one command, e.g.

· the command code
· the handler routine to execute the command
· the access rights needed
· whether semaphore protection is necessary or not

The Command Descriptor Table is allocated and initialized during xxxDrv at driver installation.2

The xxxIoctl routine processes the command by looking for the code in the command table3, checking the access rights, performing the semaphore protection if required and calling the appropriate handler routine, which is part of the command layer.

The command layer routine checks the arguments, executes the command by calling the appropriate sequence of device layer routines and returns a status code which is passed up the call stack to the user.

3 DRIVER FILES AND DIRECTORIES

The file and directory structure together with naming conventions are conform with the VLT Programming Standards [1]. This chapter defines the directory structure and the files contained therein tailored for LCU drivers.

7 A module "xxx" (literally) is provided to serve as a driver template.
It is recommended to use it as starting-point for new developments.

3.1 Driver Directory Structure

All driver related files are kept under one module root directory in several subdirectories.

The utility getTemplate should be used to generate empty instances of directory structures.

The module directory tree for the driver xxx is shown as an example:

· xxx/
The module root directory contains normally the subdirectories only, additionally a ChangeLog file (supported by the Emacs editor) should be placed here.
· xxx/include
contains all driver header files
· xxx/src
contains the Makefile and all driver source files and installation scripts
· xxx/test
contains another Makefile and all source files for test-functions. There should be an automatic test-procedure.
· xxx/config
for configuration data files
· xxx/bin, xxx/object, xxx/doc, xxx/man
contain the files produced by a make-run, where xxx/bin gets the final modules and scripts
· xxx/tmp
for anything else

The test subdirectory can contain several other complete instances of directory structures, e.g. for independent test-programs, although this should be avoided.

3.2 Driver Header Files

All header files must follow the rules described in [1]. They are stored in the include subdirectory.

The utility getTemplate should be used to generate empty instances of header files.

For drivers there are three mandatory interface header files:

· xxx.h
· xxxCommands.h
· xxxErrors.h

Two other header files are optional, but recommended:

· xxxDevice.h
· xxxPrivate.h

Any further header files could be added if appropriate.

7 Before writing a interface header file, see section 5.7 for some restrictions!

3.2.1 Guidelines for xxx.h

It shall contain:

· Includes for all other headers which are needed to call one of the driver's public functions. These are normally at least lcudrvPublic.h, xxxCommands.h, xxxErrors.h and possibly VxWorks headers. lcudrv.h should not be included, because it contains private data.
· ANSI-style declarations of the driver's installation functions xxxDrv() and xxxDevCreate().
· ANSI-style declarations of the driver's tool-functions (functions normally used for test-purposes). They should take up to one parameter. See section 5.7.1 for additional information.
· External declarations for public variables.

It shall not contain:

· Declarations of functions or macros which are not public, but only used inside the driver.

3.2.2 Guidelines for xxxCommands.h

It shall contain:

· Macro definitions for the driver's command literals. They must begin with `xxxCMD_'. The assigned number should not interfere with ioctl-function-codes already defined by VxWorks in the file ioLib.h, to keep them above 100 should be sufficient.4
· The command-macros should be arranged in functional groups, each with a comment above it.
· If required: typedefs for special structures which are necessary to call certain ioctl-commands.

It shall not contain:

· Any non-public data

3.2.3 Guidelines for xxxErrors.h

It shall contain:

· Macro definitions for the driver's error literals. They must begin with `xxxERROR_'. The assigned number must be negative and must not interfere with the error-codes already defined in the file lcudrvPublic.h, to keep them below -100 should be sufficient.5

It shall not contain:

· Any non-public data

3.2.4 Guidelines for xxxDevice.h

7 All hardware-related data must be declared as `volatile'!

It shall contain:

· Device-dependent typedefs for structures which map to hardware-registers (volatile!). 6
· Device-dependent macros (volatile!).

It shall not contain:

· Not hardware-dependent data, like function declarations etc.

3.2.5 Guidelines for xxxPrivate.h

It shall contain:

· Includes for headers which are needed only inside the driver. These are normally lcudrv.h, lculog.h and several VxWorks headers.
· ANSI-style declarations of the driver's internal functions.
· Declarations for internal variables.
· Typedefs for internally used structures, like the Device Descriptor Table (see section 4.2), possibly a Command Descriptor Table (see section 4.3), etc.
· Macro definitions for special command-codes which shall not be public.

It shall not contain:

· Hardware-dependent data.

3.3 Driver Source Files

All C source files must follow the rules described in [1]. They are stored in the src subdirectory.

The utility getTemplate should be used to generate empty instances of C source files.

For drivers there are the following mandatory source files:

· xxxDrv.c - to install the driver in the system
· xxxDevCreate.c - to add a device to the driver
· xxxOpen.c - contains driver specific routine called by open()
· xxxClose.c - contains driver specific routine called by close()
· xxxIoctl.c - contains driver specific routine called by ioctl()

Any further source files could be added if appropriate, for instance:

· xxxWrite.c - for devices which support write()
· xxxCommands.c - which contains device layer functions

Guidelines for the mandatory files are given below. Some of the code examples make use of routines and data structures which are described in chapter 4.

3.3.1 Guidelines for xxxDrv.c

The driver's installation routine is called once at system start-up.

It shall have the following prototype:

int xxxDrv(int devices, int channels, int timeout)

with the parameters:

· Maximum number of devices that shall be handled by the driver
· Maximum number of channels that can be opened at a time to all devices
· Maximum time in ticks for semaphore waits

It shall perform the following actions:

· Check if the driver is already installed, exit if yes
· Allocate memory for the Device Descriptor Table and initialize it
· Allocate memory for the Driver Channel Table (DCT) and initialize it
· Allocate memory for the Command Descriptor Table (DCT) and initialize it (optional)
· Create driver-level semaphores
· Install the driver in the system by calling iosDrvInstall()
· Install driver-level reboot-hooks

Skeleton of a driver installation routine:

#include "xxx.h"
#include "xxxPrivate.h"
#include "xxxDevice.h"
#include "rebootLib.h"


/*
* Global variables of the driver
* Initialized by installation routines, read-only for all other functions.
*/
int xxxDriverNumber = -1; /* VxWorks xxx driver number */
int xxxMaxChannels; /* max. number of open channels */
int xxxMaxDevices; /* max. number of supported devices */
int xxxTimeout; /* access timeout in ticks */
lcudrvDCT_ENTRY *xxxDCT; /* DCT of xxx driver */
xxxDEVICE_DESCRIPTOR *xxxDevTable; /* xxx Device Descriptor Table */
xxxCMD_DESCRIPTOR *xxxCmdTable; /* Command Descriptor Table */
int xxxNumCommands; /* number of commands */


#define FNAME "xxxDrv"
int xxxDrv(int devices, int channels, int timeout)
{
int status;

/*
* Check whether driver already installed
*/
if (xxxDriverNumber >= 0) return(lcudrvERROR_DRIVER_EXISTS);

/*
* Initialize device handling
*/
if (devices < 1) return(lcudrvERROR_INVALID_ARGUMENT);
xxxMaxDevices = devices;
xxxDevTable = calloc(xxxMaxDevices, sizeof(xxxDEVICE_DESCRIPTOR));
if (xxxDevTable == NULL) return(lcudrvERROR_NO_MEMORY);

/*
* Initialize Command Descriptor Table and driver specific data structures
*/
xxxTimeout = timeout;
status = xxxInitCmdTable(&xxxNumCommands, &xxxCmdTable);
if (status != lcudrvOK) return status;

/*
* Allocate and initialize the DCT
*/
if (channels < 1) return(lcudrvERROR_INVALID_ARGUMENT);
xxxMaxChannels = channels;
status = lcudrvInitDCT(&xxxDCT,xxxMaxChannels);
if (status != lcudrvOK) return(status);

/*
* Install the driver's I/O routines
* This is done at last to have the driver been not installed in the
* I/O system when one of the previous actions failed.
*/
xxxDriverNumber = iosDrvInstall(0,0,xxxOpen,xxxClose,0,0,xxxIoctl);
if (xxxDriverNumber < 0) return(lcudrvERROR_NO_MEMORY);

/*
* Install a reboot-hook to shut down all device on reboot
* THIS IS OPTIONAL.
*/
if (rebootHookAdd((FUNCPTR)xxxRebootHook) == ERROR)
{
LCU_LOG_error("%s: Cannot install reboot-hook", (int)FNAME, 0,0,0,0,0);
return(lcudrvERROR_NO_MEMORY);
}

LCU_LOG_log("%s: Driver installation OK.", (int)FNAME, 0,0,0,0,0);

return(lcudrvOK);
}

Global variables for the driver - if needed - shall be defined in this file, but:

7 Global variables should be avoided...

unless the following conditions are made sure:

· the global variables are accessed by tasks `Read-Only'
· the global variables are semaphore-protected against simultaneous write-access (section 2.7)

3.3.2 Guidelines for xxxDevCreate.c

The device installation routine is called once for each device at system start-up.

It shall have a prototype similar to:

int xxxDevCreate( char *devName,
void *baseAddrA24, void *baseAddrA16,
int intrNumber,
int intrLevel, ...)

with the parameters:

· Device-name string
· One or more base-addresses of the device
· One or more interrupt vectors to be used
· The interrupt level to be used
· Other device-specific parameters like version codes etc.
7 All base-addresses given as installation parameters shall be VMEbus addresses
in the respective address-space (A16, A24, A32), and not as seen from the CPU.
The addresses shall be converted by the installation function to improve portability.

It shall perform the following actions:

· Check if the driver is already installed, exit with error if not
· Check the device name and other parameters
· Add device to the system and initialize its device descriptor
· Create device-level semaphores
· Connect and initialize device-level interrupts
· Install device-level reboot-hooks
· Reset the device, but only if possible without influence on other devices

Skeleton of a device installation routine:

#include "xxx.h"
#include "xxxPrivate.h"
#include "xxxDevice.h"

#define DEV_PREFIX "/xxx" /* mandatory device-name prefix */
#define FNAME "xxxDevCreate" /* name of this function */
#include "lcudrvCheckDev.h" /* include inline check functions */

int xxxDevCreate(char *devName, void *baseAddrA24, void *baseAddrA16,
int intrNumber, int intrLevel, ...)
{
int unit;
int status;
xxxDEVICE_DESCRIPTOR *device;
int semOptions = SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE;
char *localA24; /* base address of the board in the CPU's address space */
char *localA16; /* base address of the board in the CPU's address space */

/*
* Device Pre-Installation Checks:
* 1. Check whether the driver is installed, exit if not.
* 2. Check device name and get unit number, exit if wrong.
* 3. Convert given bus-address to local CPU-addresses,
* allocate board memory if special base-address is given.
* 4. Check if board is actually present, exit if not.
*/
status = lcudrvCheckDevDriver(xxxDriverNumber);
if (status != lcudrvOK)
return status;

status = lcudrvCheckDevName(devName, xxxMaxDevices, &unit);
if (status != lcudrvOK)
return status;

status = lcudrvCheckDevBaseAddrA24(baseAddrA24,sizeof(xxxDPRAM),&localA24);
if (status != lcudrvOK)
return status;

status = lcudrvCheckDevBaseAddrA16(baseAddrA16, 0x100, &localA16);
if (status != lcudrvOK)
return status;

status = lcudrvCheckDevProbe(localA24); /* might be other probe-address! */
if (status != lcudrvOK)
return status;

/*
* Add device to system and initialize device descriptor
*/
device = &(xxxDevTable[unit]);
status = lcudrvInitDevice(devName, xxxDriverNumber, &(device->header));
if (status != lcudrvOK) return(status);
device->baseAddrA24 = baseAddrA24;
device->baseAddrA16 = baseAddrA16;
device->intrNumber = intrNumber;
device->intrLevel = intrLevel;
device->dpramAddr = localA24;
/* ... init other fields in the device descriptor ... */

/*
* Create the access protection semaphore
*/
device->semAccess = semMCreate(semOptions);
if (device->semAccess == NULL)
return lcudrvERROR_NO_SEMAPHORE;

/*
* Create a watch-dog timer
*/
device->wd = wdCreate();
if (device->wd == NULL)
return lcudrvERROR_NO_MEMORY ;

/*
* Perform semaphore protection
*/
if (semTake(device->semAccess, xxxTimeout) == ERROR)
return lcudrvERROR_TIMEOUT;

/*
* Reset the device
*/
/* ... connect interrupts ... */
status = xxxCmdReset(device, NULL);
/* ... other operation on the device (don't return without semGive!) ... */

/*
* Release semaphore protection
*/
semGive(device->semAccess);

if (status != lcudrvOK)
return status;

return lcudrvCheckDevReturnOK(devName);
}

3.3.3 Guidelines for xxxOpen.c

The driver's open routine is called from the VxWorks I/O system. It calls the lcudrvOpen routine to perform the common tasks and then does the driver specific work.

Skeleton of a driver open routine:

int xxxOpen(void *device, char *remainder, int mode, int *status)
{
    int channel, error;

    /*
     * Common actions
     */
    error = lcudrvOpen(device,remainder,mode,xxxDCT,xxxMaxChannels,&channel);
    if (status != NULL) *status = error;
    if (error != lcudrvOK) return(lcudrvERROR);

    /*
     * Driver specific actions
     */
    /* ... */

    return(channel);
}

The common lcudrvOpen routine must be called with the same parameters as the xxxOpen itself followed by the address of the driver's DCT and the maximum number of channels (i.e. number of DCT slots). For a more detailed description of the parameters see section 4.1.

This concept results in a call stack as shown below:

user -> system: fd = open(deviceName, openMode, &status);

system -> driver: channel = xxxOpen(&device, remainder, openMode, &status);

driver -> lcudrv: *status = lcudrvOpen(device,remainder,openMode,...,&channel);

Note: if the xxxOpen routine runs into an error condition after lcudrvOpen has already returned with status OK then lcudrvClose must be called from xxxOpen to re-do the previous lcudrvOpen.

Note: The status-pointer passed to the driver-specific open function is the third parameter of the open-call, which is not declared as (int *) but as (int) in VxWorks. If the status value is not needed then a NULL or 0 in this place should be allowed, therefore a check for NULL should be done in xxxOpen (like in the above example).

3.3.4 Guidelines for xxxClose.c

The driver's close routine is called from the VxWorks I/O system. It calls the lcudrvClose routine to perform the common tasks and then does the driver specific work.

Skeleton of a driver close routine:

int xxxClose(int channel)
{
    int status;

    /*
     * Common actions
     */
    status = lcudrvClose(xxxDCT, xxxMaxChannels, channel);

    if (status != lcudrvOK)
        {
        /*
         * Driver specific error processing
         */
        /*... */
        }
    else
        {
        /*
         * Normal processing: driver specific actions
         */
        /*... */
        }

    /*
     * Return completion status
     */
    return(status);
}

The common lcudrvClose routine must be called with the address of the driver's DCT and the number of the channel to be closed.

This concept results in a call stack as shown below:

user -> system: status = close(fd);

system -> driver: status = xxxClose(channel);

driver -> lcudrv: status = lcudrvClose(xxxDCT, xxxMaxChannels, channel);

whereby the status is passed backwards from the lowest level to the user.

3.3.5 Guidelines for xxxIoctl.c

The driver's ioctl routine is called from the VxWorks I/O system.

Skeleton of a driver ioctl routine:

int xxxIoctl(int channel, int command, void *argument)
{
    int status;
    timCMD_DESCRIPTOR *cmd;
    timDEVICE_DESCRIPTOR *device;
    lcudrvDEVICE_HEADER *header;

    /*
     * Check if command code valid and find command descriptor
     */
    cmd = ... /* driver-specific */

    /*
     * Check the channel and the access rights for this command,
     * get the device descriptor
     */
    status = lcudrvCheckAccess(xxxDCT,xxxMaxChannels,channel,cmd->access,
                               &header);
    if (status != lcudrvOK) return(status);
    device = (void *)header;

    /*
     * Further driver-specific actions:
     * - Perform semaphore protection
     * - Execute the command
     * - Release semaphore protection
     */

    return(status);
}

There is no common lcudrvIoctl routine. Only the routine lcudrvCheckAccess should be used for common actions, namely to check the channel number and to make sure that the access rights are suffcient, i.e. the requested command is allowed to execute with the open-mode of this channel. The

function returns the appropriate error if the channel number is invalid or the access rights are insufficient. If there are no access rights assigned to the commands then lcudrvCheckChannel can be used instead of lcudrvCheckAccess, omitting the rights check.

The routine shall return lcudrvOK if successful or a negative error code if any problem occurred.

This concept results in a call stack as shown below:

user -> system: status = ioctl(fd, command, (int)argument);

system -> driver: status = xxxIoctl(channel, command, argument);

whereby the status is passed backwards from the lowest level to the user.

3.4 Makefiles

A Makefile must follow the rules described in [1]. One Makefile can be contained in the src and test subdirectories respectively. There are no further rules for driver-makefiles.

The utility getTemplate should be used to generate an empty instance of a Makefile.

3.5 Sample Installation Scripts

A simple script file with the name

xxxInstall

should be provided which executes under the VxWorks shell an performs all necessary actions to install the driver and its devices:

· Loading of the module
· Call of xxxDrv() to install the driver
· Call(s) of xxxDevCreate() to install the devices
· Spawning of tasks necessary for operation

However, this script only serves as an example, and is not actually used. Unlike the following:

3.6 Module Boot Scripts for Use with lcuboot

With the lcuboot module there is also the possibility to install driver and devices - but also arbitrary LCU modules in general - automatically. For this reason a VxWorks script named

xxx.boot

shall be provided in the src sub-directory of any LCU module and installed as public SCRIPT into the bin target directory, which is used at start-up time by the LCU to verify if there are any devices present, and then to install them appropriately.

This is a simple dummy:

#******************************************************************************
# E.S.O. - VLT project
#
# "@(#) $Id: xxx.boot,v 1.6 1995/07/11 10:48:31 ssandroc Exp $"
#
# VxWorks script for automatic installation of driver and devices
#
xxxN = lcubootAutoDevRegister("/xxx0",0,1,1,1)

lcubootAutoDrvInstall "xxx"

lcubootAutoDevCreate "/xxx0",-1,-1,0,0

An actual implementation of such a driver boot-script, that supports fully automatic driver/device installation, is shown below:

#******************************************************************************
# E.S.O. - VLT project
#
# "@(#) $Id: acro.boot,v 1.11 1995/11/07 21:32:25 ssandroc Exp $"
#
# VxWorks script for automatic installation of driver and devices
#
# who when what
# -------- ---------- ----------------------------------------------
# ssandroc 24.04.1995 created
#
acroN = lcubootAutoDevRegister("/acro0",0x2d,0x1000,1,1,1)
acroN = lcubootAutoDevRegister("/acro1",0x2d,0x1400,1,1,1)
acroN = lcubootAutoDevRegister("/acro2",0x2d,0x1800,1,1,1)
acroN = lcubootAutoDevRegister("/acro3",0x2d,0x1c00,1,1,1)
acroN = lcubootAutoDevRegister("/acro4",0x2d,0x2000,1,1,1)
acroN = lcubootAutoDevRegister("/acro5",0x2d,0x2400,1,1,1)
acroN = lcubootAutoDevRegister("/acro6",0x2d,0x2800,1,1,1)
acroN = lcubootAutoDevRegister("/acro7",0x2d,0x2c00,1,1,1)

lcubootAutoDrvInstall "acro"

lcubootAutoDevCreate "/acro0",0x1000,112,1
lcubootAutoDevCreate "/acro1",0x1400,113,1
lcubootAutoDevCreate "/acro2",0x1800,114,1
lcubootAutoDevCreate "/acro3",0x1c00,115,1
lcubootAutoDevCreate "/acro4",0x2000,116,1
lcubootAutoDevCreate "/acro5",0x2400,117,1
lcubootAutoDevCreate "/acro6",0x2800,118,1
lcubootAutoDevCreate "/acro7",0x2c00,119,1

lcubootAutoSpawn acroN,"tintACRO",200,0x18,2000,"acroInt"
# ___oOo___

It is important to recognize that standard address (and other configuration) settings must be defined, and followed by all devices, in order to make this work.

For non-trivial cases there should be a man-page `xxx.boot(5)' provided by the driver, that explains possible user configurations.

See [7] for more information about the lcuboot functions.

3.7 Device Documentation Man-Page

There shall be a man-page `xxx(4)' provided by the driver that explains the device's hardware and possible user configurations, like jumper settings etc.

See existing man-pages for examples.

1
What applies to ioctl can be transferred accordingly to the other I/O calls read and write. They are not described here, because ioctl is normally the only call used in connection with device drivers.

2
There might be variants to the C.D.T.-concept which are more appropriate for a specific driver.

3
For efficiency reasons the command-code should be used as index into the C.D.T., if possible.

4
Some of the VxWorks defined codes are handled inside ioctl itself and are not passed to the driver-specific ioctl-routine. In VxWorks 5.1.1 at least for FIOGETNAME (number 18). There is probably no reliable documentation which codes are handled in this way.

5
CCS and LCC require that error-macros begin with `xxxERR_' and have positive values. For historical reasons all existing drivers do not obey this convention but use `xxxERROR_' and negative values instead.

6
Note that in `typedef volatile struct S {...} T;' the `volatile' is not propagated by the structure tag `S', but only by the type name `T'. The members of the structure can additionally be declared as volatile.



Quadralay Corporation
http://www.webworks.com
Voice: (512) 719-3399
Fax: (512) 719-3606
sales@webworks.com
TOC PREV NEXT INDEX