This chapter describes the simulator BSP and includes the following sections:
"UNIX Signals" describes how signals are used by the ChorusOS Simulator and how they are classified.
"Use of UNIX Services" describes how to access system functions by making indirect system calls.
"Writing a Device Driver" describes how to write a device driver.
One of the ways supervisor actors communicate with the Solaris Operating Environment, through ChorusOS APIs, is by using signals.
The ChorusOS 4.0.1 Simulator for the Solaris Operating Environment (SPARC Platform Edition) converts UNIX signals into interrupts and exceptions. As a result, processes outside the ChorusOS operating system can signal to the ChorusOS process in order to generate asynchronous events. These events are then caught by supervisor processes through the standard ChorusOS interrupt management interface. Supervisor processes can also use the exception management interface to handle exception-type signals, just as they would for native exceptions on native hardware.
Table 3-1 illustrates how UNIX signals are classified.
Table 3-1 UNIX Signal Classification
Signals which are not described in this table (those up to SIGRTMAX) are ignored.
Although it is possible for actors to make UNIX system calls directly, you should avoid doing this because some calls change the state of processes, leading to unpredictable behavior from the ChorusOS operating system. Examples of these system calls are:
A blocking read blocks an entire process.
Certain library calls change the signal mask.
In addition, the ChorusOS embedded library may conflict with the Standard C library because there are function names in both libraries which are identical.
UNIX system calls can be called indirectly, without linking with their corresponding libraries, by using a special structure whose fields are pointers to function entry points. However, this feature limits what functions can be accessed to the following:
fcntl() (with some restrictions regarding arguments). Here is an extract from exec/chLoader.h:
int (*fcntl)(int, int); |
int (*fcntl1)(int, int, int); |
ioctl() (with some restrictions regarding arguments). Here is an extract from exec/chLoader.h:
int (*ioctl1)(int, int, void *); |
int (*ioctlInt)(int, int, int); |
sscanf() (with some restrictions regarding arguments). Here is an extract from exec/chLoader.h:
int (*sscanf1)(const char*, const char*, void *); |
int (*sscanf4)(const char*, const char*, void *, void *, void *, void *); |
Example 3-1 illustrates how to retrieve a structure containing addresses of UNIX function calls which can later be used to make indirect system calls. This structure is stored as a property called PROP_CPU_FUNCTION_ARRAY which is attached to the CPU node of the device tree.
/* include files */ #include <cpu/unixProp.h> #include <exec/chLoader.h> . . /* declaration of pointer for address handling */ static UnixFct* unixFct; . . DevProperty prop; void** tmpPtr; /* looking for the CPU node in the device tree */ cpuNode = dtreeNodeFind(dtreeNodeRoot(), NODE_CPU); if (cpuNode == NULL) { DKI_ERR(("No NODE_CPU in the device tree\n")); return; } /* Retrieve PROP_CPU_FUNCTION_ARRAY property */ prop = dtreePropFind(cpuNode, PROP_CPU_FUNCTION_ARRAY); if (prop == NULL) { DKI_ERR(("No PROP_CPU_FUNCTION_ARRAY property in the NODE_CPU node\n")); return; } /* reading of its value */ tmpPtr = dtreePropValue(prop); if (tmpPtr == NULL) { DKI_ERR(("No PROP_CPU_FUNCTION_ARRAY value in the NODE_CPU node\n")); return; } unixFct = *tmpPtr;
Access your UNIX functions using the -> structure operator:
unixFct->function_name
function_name is the name of the function you want to call.
Please refer to the exec/chLoader.h header file for a list of types and parameters. Note that some types and constants have been prefixed to avoid conflicts with ChorusOS types and constants.
The architecture of the ChorusOS 4.0.1 Simulator for the Solaris Operating Environment (SPARC Platform Edition) is similar to ChorusOS 4.0.1 running on a physical target, except that direct access to hardware is not possible. Hardware can only be accessed through UNIX APIs based on UNIX file descriptors.
A typical example of a device driver would be one to emulate serial communication by using the read() and write() UNIX functions.
The success or failure of I/O operations are not signalled by interrupts as they are on a conventional target, but by a single UNIX signal, SIGIO
. Instead of attaching an interrupt, drivers attach a file descriptor to the interrupt handler.
Here is the procedure for writing a device driver using file descriptors for I/O access:
Retrieve the structure containing the UNIX function addresses.
Attach a file descriptor to the interrupt handler by calling the intr_attach() function.
Handle the interrupts by calling the appropriate UNIX functions.
Please refer to the ChorusOS 4.0 Device Driver Framework Guide for details on writing a device driver.
The following sections follow the same format as "Writing Bus Drivers" in ChorusOS 4.0 Device Driver Framework Guide. Only the differences are described.
In addition to including the header files for the DKI and DDI APIs involved in your device driver's implementation, you must also include the appropriate header file to access UNIX function calls. See "Retrieving UNIX Function Addresses" for more details.
The simulator BSP is fully compliant with the standard BSP.
The simulator BSP is fully compliant with the standard BSP.
There are two differences that you need to be aware of when writing registry functions for your device driver.
When accessing the device using UNIX function calls, you must perform these additional tasks:
When attaching to the interrupt handler, which processes the SIGIO
signal, you must perform these additional tasks:
In the init() function, the UNIX function structure pointer must be retrieved from the device tree (see "Retrieving UNIX Function Addresses").
The specific part of the init() function of the UNIX function structure pointer is retrieved from the device tree as follows:
device->fd = unixFct->open("/dev/tty0", UNIX_O_RDWR, 0); if (device->fd == -1) { DKI_ERR(("%s: /dev/tty0 open failed (%d)\n", path, unixFct->geterrno())); return; } /* Open SIGIO Parent Class */ res = sigioOps->open((BusId)busId, myNode, eventHandler, NULL, device, &device->busDevId); if (res != K_OK) { DKI_ERR(("%s: parent bus open() failed (%d)\n", path, res)); return; } /* Connect Interrupt Handler based on file descriptor */ res = sigioOps->intr_attach(device->busDevId, (void *)device->fd, (BusIntrHandler)intrHandler, device, &device->busIntrOps, &device->busIntrId); if (res != K_OK) { DKI_ERR(("%s: intr_attach failed (%d)\n", path, res)); return; }
In the init() function, a device must be opened by calling the open() UNIX function.
The interrupt number parameter is replaced by the file descriptor when calling the intr_attach() function.
The simulator BSP is fully compliant with the standard BSP. However, the shutdown related events, specified by the common bus API, are not implemented:
SYS_SHUTDOWN
DEV_SHUTDOWN
DEV_REMOVAL