VxWorks Programmers Guide5.5
VxWorks Programmers Guide5.5
VxWorks Programmers Guide5.5
VxWorks
Programmers Guide
EDITION 2
Corporate Headquarters
Wind River Systems, Inc.
500 Wind River Way
Alameda, CA 94501-1153
U.S.A.
toll free (U.S.): (800) 545-WIND
telephone: (510) 748-4100
facsimile: (510) 749-2010
For additional contact information, please visit the Wind River URL:
http://www.windriver.com
For information on how to contact Customer Support, please visit the following URL:
http://www.windriver.com/support
Introduction ............................................................................................................. 1
Basic OS .................................................................................................................. 7
10
11
12
iii
iv
Contents
Introduction ..............................................................................................................
1.1
Overview ...........................................................................................................
1.2
1.3
1.4
1.5
Basic OS ....................................................................................................................
2.1
Introduction ......................................................................................................
2.2
2.2.1
Multitasking ........................................................................................
2.2.2
2.2.3
10
11
12
13
13
14
VxWorks 5.5
Programmers Guide
2.2.4
14
14
15
15
16
17
18
19
2.2.5
21
2.2.6
22
22
23
23
24
2.2.7
24
2.2.8
25
27
27
28
29
30
32
2.3.1
32
2.3.2
33
33
34
Semaphores ........................................................................................
34
35
36
40
43
44
45
2.2.9
2.3
2.3.3
vi
2.3.4
47
48
50
50
51
2.3.5
Pipes .....................................................................................................
53
2.3.6
53
Sockets .................................................................................................
Remote Procedure Calls (RPC) ........................................................
53
54
Signals ..................................................................................................
55
55
56
57
2.4.1
58
58
58
59
59
59
60
60
61
62
62
62
63
2.5
64
2.6
65
2.6.1
66
2.6.2
67
2.6.3
67
2.3.7
2.4
2.4.2
2.4.3
vii
VxWorks 5.5
Programmers Guide
2.6.4
68
2.6.5
69
2.6.6
70
2.6.7
70
2.6.8
71
73
3.1
Introduction ......................................................................................................
73
3.2
73
3.3
74
3.4
75
3.4.1
76
76
76
76
77
77
78
78
79
3.4.2
80
3.4.3
80
81
3.5.1
82
3.5.2
82
3.5.3
84
3.5.4
84
3.5
3.6
85
3.6.1
86
viii
3.6.2
87
3.6.3
89
3.7
92
3.8
94
3.8.1
94
3.8.2
95
3.8.3
97
3.8.4
98
3.8.5
3.9
4
4.1
4.2
4.3
4.3.2
4.3.3
4.3.4
4.3.5
4.3.6
4.3.7
4.3.8
ix
VxWorks 5.5
Programmers Guide
4.4
4.5
4.6
4.4.2
4.5.2
4.5.3
4.6.2
4.6.3
4.7
4.7.2
132
133
133
135
4.7.3
4.7.4
4.7.5
4.7.7
141
143
144
144
4.7.8
4.8
4.9
4.9.2
4.9.3
4.9.4
164
165
168
168
168
172
xi
176
178
178
181
182
VxWorks 5.5
Programmers Guide
182
183
183
184
185
186
186
186
187
187
188
188
189
189
190
4.10
4.11
5.1
5.2
5.2.2
5.2.3
5.2.4
5.2.5
5.2.6
5.2.7
5.2.8
xii
5.2.9
5.2.10
5.2.11
5.2.12
5.2.13
5.2.14
5.2.15
5.2.16
5.3
Booting from a Local dosFs File System Using SCSI .................................. 221
5.4
5.4.2
5.4.3
5.4.4
5.4.5
5.4.6
xiii
VxWorks 5.5
Programmers Guide
5.4.7
5.5
5.5.2
5.5.3
5.5.4
5.5.5
5.5.6
5.6
5.7
238
239
239
239
6.1
6.2
6.2.2
6.2.3
6.2.4
Loading and Unloading Object Modules from the Target Shell . 245
6.2.5
6.2.6
6.2.7
xiv
6.2.8
6.3
6.3.2
6.3.3
6.3.4
6.3.5
6.3.6
6.4
256
257
258
259
260
6.4.2
6.4.3
6.4.4
6.4.5
6.4.6
xv
VxWorks 5.5
Programmers Guide
6.5
6.6
270
271
272
272
273
273
7.1
7.2
7.2.2
7.2.3
7.3
7.3.2
7.4
7.4.2
282
282
282
282
xvi
7.5
7.4.3
7.4.4
7.6
289
289
289
289
7.5.2
7.5.3
7.7
8
8.1
8.1.2
8.2
8.3
8.4
8.5
8.5.2
xvii
VxWorks 5.5
Programmers Guide
8.6
8.7
8.5.3
8.5.4
8.5.5
8.5.6
8.5.7
8.6.2
8.7.2
8.7.3
8.8
8.9
8.10
8.10.2
8.11
xviii
8.11.3
8.12
8.12.2
8.12.3
Writing the MTD Read, Write, and Erase Functions ..................... 333
Read Routine ...................................................................................... 333
Write Routine ...................................................................................... 334
Erase Routine ...................................................................................... 335
8.12.4
8.12.5
8.13
8.13.2
8.13.3
xix
VxWorks 5.5
Programmers Guide
8.13.4
8.13.5
343
344
344
344
9.1
9.2
9.2.2
9.3
347
347
348
348
9.3.2
9.4
9.3.3
9.3.4
9.4.2
xx
9.4.3
9.5
9.6
9.6.2
9.6.3
9.7
362
363
364
365
9.7.2
9.8
9.8.2
9.8.3
9.9
9.10
xxi
VxWorks 5.5
Programmers Guide
9.10.2
9.10.3
9.11
10
9.10.4
9.10.5
9.11.2
9.11.3
9.11.4
9.11.5
9.11.6
9.11.7
9.11.8
10.1
10.2
10.3
10.3.2
10.3.3
10.3.4
10.3.5
10.3.6
xxii
10.3.7
10.4
10.5
10.6
10.7
10.6.1
10.6.2
10.7.2
11
10.7.3
10.7.4
10.7.5
10.7.6
11.1
11.2
11.2.2
11.2.3
11.2.4
xxiii
444
445
445
449
451
VxWorks 5.5
Programmers Guide
11.3
11.4
11.5
12
11.3.2
11.3.3
11.3.4
11.3.5
454
11.4.2
11.4.3
11.4.4
11.4.5
11.5.2
12.1
12.2
12.3
12.4
12.5
12.5.2
xxiv
12.5.3
12.5.4
12.5.5
12.5.6
xxv
VxWorks 5.5
Programmers Guide
xxvi
1
Introduction
1.1 Overview
This manual describes the VxWorks real-time operating system, and how to use
VxWorks facilities in the development of real-time applications. This manual
covers the following topics, focusing first on basic product functionality and
facilities, then on optional products and technologies:
I/O system
target tools, such as the shell, target-based loader, and target symbol table
This chapter describes where to find related documentation about VxWorks and
the Tornado development environment. In addition, it describes Wind River
customer services, and the document conventions followed in this manual.
VxWorks 5.5
Programmers Guide
The Tornado Getting Started Guide provides information about installing the
Tornado development environment and associated optional products.
The Tornado Users Guide provides procedural information about setting up the
development environment, and about using Tornado tools to develop
VxWorks applications. It includes information on configuring VxWorks
systems with the various components described in this guide, and on building
and running those systems.
1. For example, VxWorks for PowerPC Architecture Supplement, VxWorks for Pentium Architecture
Supplement, VxWorks for MIPS Architecture Supplement, and VxWorks for ARM Architecture
Supplement.
1
Introduction
NOTE: In this book, as well as in the VxWorks API Reference, VxWorks components
are identified by the name used in system configuration files, in the form of
INCLUDE_FOO. Similarly, configuration parameters are identified by their
configuration parameter names, such as NUM_FOO_FILES.
VxWorks 5.5
Programmers Guide
Table 1-1
Typographical Conventions
Term
Example
files, pathnames
/etc/hosts
libraries, drivers
memLib, nfsDrv
host tools
more, chkdsk
subroutines
semTake( )
boot commands
code display
main ();
keyboard input
display output
value = 0
user-supplied parameters
name
INCLUDE_NFS
#define
RETURN
control characters
CTRL+C
lower-case acronyms
fd
Cross-References
The cross-references that appear in this guide for subroutines, libraries, or tools
refer to entries in the VxWorks API Reference (for target routines or libraries) or in
the in the Tornado Users Guide (for host tools). Cross-references to other books are
made at the chapter level, and take the form Book Title: Chapter Name; for example,
Tornado Users Guide: Workspace.
For information about how to access online documentation, see the Tornado Getting
Started Guide: Documentation Guide.
Directory Pathnames
All VxWorks files reside in the target directory (and its subdirectories), directly
below the base Tornado installation directory. Because the installation directory is
determined by the user, the following format is used for pathnames:
installDir/target.
1
Introduction
UNIX and Windows filenames since this is the default for VxWorks.
VxWorks 5.5
Programmers Guide
2
Basic OS
2.1 Introduction
Modern real-time systems are based on the complementary concepts of
multitasking and intertask communications. A multitasking environment allows a
real-time application to be constructed as a set of independent tasks, each with its
own thread of execution and set of system resources. The intertask communication
facilities allow these tasks to synchronize and communicate in order to coordinate
their activity. In VxWorks, the intertask communication facilities range from fast
semaphores to message queues and from pipes to network-transparent sockets.
Another key facility in real-time systems is hardware interrupt handling, because
interrupts are the usual mechanism to inform a system of external events. To get
the fastest possible response to interrupts, interrupt service routines (ISRs) in
VxWorks run in a special context of their own, outside any tasks context.
This chapter discusses the tasking facilities, intertask communication, and the
interrupt handling facilities that are at the heart of the VxWorks run-time
environment. You can also use POSIX real-time extensions with VxWorks. For
more information, see 3. POSIX Standard Interfaces.
VxWorks 5.5
Programmers Guide
task, but with some additional features. For details, see 3.4 POSIX Threads, p.75.
2.2.1 Multitasking
Multitasking provides the fundamental mechanism for an application to control
and react to multiple, discrete real-world events. The VxWorks real-time kernel,
wind, provides the basic multitasking environment. Multitasking creates the
appearance of many threads of execution running concurrently when, in fact, the
kernel interleaves their execution on the basis of a scheduling algorithm. Each task
has its own context, which is the CPU environment and system resources that the
task sees each time it is scheduled to run by the kernel. On a context switch, a tasks
context is saved in the task control block (TCB).
A tasks context includes:
In VxWorks, one important resource that is not part of a tasks context is memory
address space: all code executes in a single common address space. Giving each
task its own memory space requires virtual-to-physical memory mapping, which
is available only with the optional product VxVMI; for more information, see
12. Virtual Memory Interface.
2
Basic OS
Description
READY
The state of a task that is not waiting for any resource other than the CPU.
PEND
DELAY
SUSPEND
The state of a task that is unavailable for execution. This state is used
primarily for debugging. Suspension does not inhibit state transition,
only task execution. Thus, pended-suspended tasks can still unblock and
delayed-suspended tasks can still awaken.
DELAY + S
PEND + S
PEND + T
PEND + S + T
The state of a task that is both pended with a timeout value and
suspended.
state + I
Table 2-1 describes the state symbols that you see when working with Tornado
development tools. Figure 2-1 shows the corresponding state diagram of the wind
kernel states.
VxWorks 5.5
Programmers Guide
Figure 2-1
ready
delayed
suspended
taskInit( )
ready
ready
ready
pended
pended
delayed
delayed
suspended
suspended
suspended
pended
delayed
suspended
ready
suspended
ready
suspended
ready
pended
delayed
semTake( ) / msgQReceive( )
taskDelay( )
taskSuspend( )
semGive( ) / msgQSend( )
taskSuspend( )
expired delay
taskSuspend( )
taskResume( ) / taskActivate( )
taskResume( )
taskResume( )
10
2
Basic OS
Table 2-2
Description
kernelTimeSlice( )
taskPrioritySet( )
taskLock( )
taskUnlock( )
A preemptive priority-based scheduler preempts the CPU when a task has a higher
priority than the current task running. Thus, the kernel ensures that the CPU is
always allocated to the highest priority task that is ready to run. This means that if
a task with a higher priority than that of the current task becomes ready to run,
the kernel immediately saves the current tasks context, and switches to the context
of the higher priority task. For example, in Figure 2-2, task t1 is preempted by
higher-priority task t2, which in turn is preempted by t3. When t3 completes, t2
continues executing. When t2 completes execution, t1 continues executing.
Priority Preemption
t3
HIGH
priority
Figure 2-2
LOW
t2
t2
t1
t1
time
KEY:
= preemption
= task completion
11
VxWorks 5.5
Programmers Guide
Round-Robin Scheduling
A round-robin scheduling algorithm attempts to share the CPU fairly among all
ready tasks of the same priority. Round-robin scheduling uses time slicing to achieve
fair allocation of the CPU to all tasks with the same priority. Each task, in a group
of tasks with the same priority, executes for a defined interval or time slice.
Round-robin scheduling is enabled by calling kernelTimeSlice( ), which takes a
parameter for a time slice, or interval. This interval is the amount of time each task
is allowed to run before relinquishing the processor to another equal-priority task.
Thus, the tasks rotate, each executing for an equal interval of time. No task gets a
second slice of time before all other tasks in the priority group have been allowed
to run.
In most systems, it is not necessary to enable round-robin scheduling, the
exception being when multiple copies of the same code are to be run, such as in a
user interface task.
If round-robin scheduling is enabled, and preemption is enabled for the executing
task, the system tick handler increments the tasks time-slice count. When the
specified time-slice interval is completed, the system tick handler clears the
counter and the task is placed at the tail of the list of tasks at its priority level. New
tasks joining a given priority group are placed at the tail of the group with their
run-time counter initialized to zero.
Enabling round-robin scheduling does not affect the performance of task context
switches, nor is additional memory allocated.
If a task blocks or is preempted by a higher priority task during its interval, its
time-slice count is saved and then restored when the task becomes eligible for
execution. In the case of preemption, the task will resume execution once the
higher priority task completes, assuming that no other task of a higher priority is
ready to run. In the case where the task blocks, it is placed at the tail of the list of
tasks at its priority level. If preemption is disabled during round-robin scheduling,
the time-slice count of the executing task is not incremented.
Time-slice counts are accrued by the task that is executing when a system tick
occurs, regardless of whether or not the task has executed for the entire tick
interval. Due to preemption by higher priority tasks or ISRs stealing CPU time
from the task, it is possible for a task to effectively execute for either more or less
total CPU time than its allotted time slice.
Figure 2-3 shows round-robin scheduling for three tasks of the same priority: t1, t2,
and t3. Task t2 is preempted by a higher priority task t4 but resumes at the count
where it left off when t4 is finished.
12
2
Basic OS
Figure 2-3
Round-Robin Scheduling
t4
HIGH
priority
time slice
LOW
t1
t2
t3
t1
t2
t2
t3
time
KEY:
= preemption
= task completion
Preemption Locks
The wind scheduler can be explicitly disabled and enabled on a per-task basis with
the routines taskLock( ) and taskUnlock( ). When a task disables the scheduler by
calling taskLock( ), no priority-based preemption can take place while that task is
running.
However, if the task explicitly blocks or suspends, the scheduler selects the next
highest-priority eligible task to execute. When the preemption-locked task
unblocks and begins running again, preemption is again disabled.
Note that preemption locks prevent task context switching, but do not lock out
interrupt handling.
Preemption locks can be used to achieve mutual exclusion; however, keep the
duration of preemption locking to a minimum. For more information, see
2.3.2 Mutual Exclusion, p.33.
When using taskLock( ), consider that it will not achieve mutual exclusion.
Generally, if interrupted by hardware, the system will eventually return to your
task. However, if you block, you lose task lockout. Thus, before you return from
the routine, taskUnlock( ) should be called.
13
VxWorks 5.5
Programmers Guide
When a task is accessing a variable or data structure that is also accessed by an ISR,
you can use intLock( ) to achieve mutual exclusion. Using intLock( ) makes the
operation atomic in a single processor environment. It is best if the operation is
kept minimal, meaning a few lines of code and no function calls. If the call is too
long, it can directly impact interrupt latency and cause the system to become far
less deterministic.
All application tasks should be priority 100 - 250. However, driver support tasks
(tasks associated with an ISR) can be in the range of 51-99. These tasks are crucial;
for example, if a support task fails while copying data from a chip, the device loses
that data.1 The system netTask( ) is at priority 50, so user tasks should not be
assigned priorities below that task; if they are, the network connection could die
and prevent debugging capabilities with Tornado.
The taskSpawn( ) routine creates the new task context, which includes allocating
the stack and setting up the task environment to call the main routine (an ordinary
1. For example, a network interface, an HDLC, and so on.
14
2
Basic OS
subroutine) with the specified arguments. The new task begins execution at the
entry to the specified routine.
2
Table 2-3
Description
taskSpawn( )
taskInit( )
taskActivate( )
Task Stack
It is hard to know exactly how much stack space to allocate, without reverseengineering the system configuration. To help avoid a stack overflow, and task
stack corruption, you can take the following approach. When initially allocating
the stack, make it much larger than anticipated; for example, from 20KB to up to
100KB, depending upon the type of application. Then, periodically monitor the
stack with checkStack( ), and if it is safe to make them smaller, modify the size.
When a task is spawned, you can specify an ASCII string of any length to be the
task name. VxWorks returns a task ID, which is a 4-byte handle to the tasks data
structures. Most VxWorks task routines take a task ID as the argument specifying
a task. VxWorks uses a convention that a task ID of 0 (zero) always implies the
calling task.
VxWorks does not require that task names be unique, but it is recommended that
unique names be used in order to avoid confusing the user. Furthermore, to use the
Tornado development tools to their best advantage, task names should not conflict
with globally visible routine or variable names. To avoid name conflicts, VxWorks
15
VxWorks 5.5
Programmers Guide
uses a convention of prefixing all task names started from the target with the
character t and task names started from the host with the character u.
You may not want to name some or all of your applications tasks. If a NULL
pointer is supplied for the name argument of taskSpawn( ), then VxWorks assigns
a unique name. The name is of the form tN, where N is a decimal integer that is
incremented by one for each unnamed task that is spawned.
The taskLib routines listed in Table 2-4 manage task IDs and names.
Table 2-4
Description
taskName( )
taskNameToId( )
taskIdSelf( )
taskIdVerify( )
Task Options
When a task is spawned, you can pass in one or more option parameters, which are
listed in Table 2-5. The result is determined by performing a logical OR operation
on the specified options.
Table 2-5
Task Options
Name
Hex Value
Description
VX_FP_TASK
0x0008
VX_NO_STACK_FILL
0x0100
VX_PRIVATE_ENV
0x0080
VX_UNBREAKABLE
0x0002
VX_DSP_TASK
0x0200
VX_ALTIVEC_TASK
0x0400
16
2
Basic OS
You must include the VX_FP_TASK option when creating a task that:
For example:
tid = taskSpawn ("tMyTask", 90, VX_FP_TASK, 20000, myFunc, 2387, 0, 0,
0, 0, 0, 0, 0, 0, 0);
Description
taskOptionsGet( )
taskOptionsSet( )
Task Information
The routines listed in Table 2-7 get information about a task by taking a snapshot
of a tasks context when the routine is called. Because the task state is dynamic, the
information may not be current unless the task is known to be dormant (that is,
suspended).
Table 2-7
Description
taskIdListGet( )
taskInfoGet( )
taskPriorityGet( )
taskRegsGet( )
17
VxWorks 5.5
Programmers Guide
Table 2-7
Description
taskRegsSet( )
taskIsSuspended( )
taskIsReady( )
taskTcb( )
Tasks can be dynamically deleted from the system. VxWorks includes the routines
listed in Table 2-8 to delete tasks and to protect tasks from unexpected deletion.
Table 2-8
Task-Deletion Routines
Call
Description
exit( )
taskDelete( )
taskSafe( )
taskUnsafe( )
* Memory that is allocated by the task during its execution is not freed when the task
is terminated.
WARNING: Make sure that tasks are not deleted at inappropriate times. Before an
application deletes a task, the task should release all shared resources that it holds.
Tasks implicitly call exit( ) if the entry routine specified during task creation
returns. A task can kill another task or itself by calling taskDelete( ).
When a task is deleted, no other task is notified of this deletion. The routines
taskSafe( ) and taskUnsafe( ) address problems that stem from unexpected
deletion of tasks. The routine taskSafe( ) protects a task from deletion by other
tasks. This protection is often needed when a task executes in a critical region or
engages a critical resource.
18
2
Basic OS
NOTE: You can use VxWorks events to send an event when a task finishes
For example, a task might take a semaphore for exclusive access to some data
structure. While executing inside the critical region, the task might be deleted by
another task. Because the task is unable to complete the critical region, the data
structure might be left in a corrupt or inconsistent state. Furthermore, because the
semaphore can never be released by the task, the critical resource is now
unavailable for use by any other task and is essentially frozen.
Using taskSafe( ) to protect the task that took the semaphore prevents such an
outcome. Any task that tries to delete a task protected with taskSafe( ) is blocked.
When finished with its critical resource, the protected task can make itself available
for deletion by calling taskUnsafe( ), which readies any deleting task. To support
nested deletion-safe regions, a count is kept of the number of times taskSafe( ) and
taskUnsafe( ) are called. Deletion is allowed only when the count is zero, that is,
there are as many unsafes as safes. Only the calling task is protected. A task
cannot make another task safe or unsafe from deletion.
The following code fragment shows how to use taskSafe( ) and taskUnsafe( ) to
protect a critical region of code:
taskSafe ();
semTake (semId, WAIT_FOREVER);
.
.
/* critical region code */
.
semGive (semId);
taskUnsafe ();
/* Release semaphore */
Deletion safety is often coupled closely with mutual exclusion, as in this example.
For convenience and efficiency, a special kind of semaphore, the mutual-exclusion
semaphore, offers an option for deletion safety. For more information, see MutualExclusion Semaphores, p.40.
Task Control
The routines listed in Table 2-9 provide direct control over a tasks execution.
VxWorks debugging facilities require routines for suspending and resuming a
task. They are used to freeze a tasks state for examination.
19
VxWorks 5.5
Programmers Guide
Table 2-9
Description
taskSuspend( )
Suspends a task.
taskResume( )
Resumes a task.
taskRestart( )
Restarts a task.
taskDelay( )
nanosleep( )
The routine sysClkRateGet( ) returns the speed of the system clock in ticks per
second. Instead of taskDelay( ), you can use the POSIX routine nanosleep( ) to
specify a delay directly in time units. Only the units are different; the resolution of
both delay routines is the same, and depends on the system clock. For details, see
3.2 POSIX Clocks and Timers, p.73.
As a side effect, taskDelay( ) moves the calling task to the end of the ready queue
for tasks of the same priority. In particular, you can yield the CPU to any other
tasks of the same priority by delaying for zero clock ticks:
taskDelay (NO_WAIT);
System clock resolution is typically 60Hz (60 times per second). This is a relatively
long time for one clock tick, and would be even at 100Hz or 120Hz. Thus, since
periodic delaying is effectively polling, you may want to consider using eventdriven techniques as an alternative.
20
2
Basic OS
Description
taskCreateHookAdd( )
taskCreateHookDelete( )
taskSwitchHookAdd( )
taskSwitchHookDelete( )
taskDeleteHookAdd( )
taskDeleteHookDelete( )
Task switch hook routines must not assume any VM context is current other
than the kernel context (as with ISRs).
Task switch and swap hooks must not rely on knowledge of the current task or
invoke any function that relies on this information; for example, taskIdSelf( ).
The taskCreateAction hook routines execute in the context of the creator task, and
any new objects are owned by the creator tasks home protection domain, or the
creator task itself. It may, therefore, be necessary to assign the ownership of new
objects to the task that is created in order to prevent undesirable object reclamation
in the event that the creator task terminates.
21
VxWorks 5.5
Programmers Guide
User-installed switch hooks are called within the kernel context and therefore do
not have access to all VxWorks facilities. Table 2-11 summarizes the routines that
can be called from a task switch hook; in general, any routine that does not involve
the kernel can be called.
Table 2-11
Routines
bLib
All routines
fppArchLib
fppSave( ), fppRestore( )
intLib
lstLib
mathALib
rngLib
taskLib
vxLib
vxTas( )
NOTE: For information about POSIX extensions, see 3. POSIX Standard Interfaces.
22
2
Basic OS
__errno( )that returns the address of the global variable, errno (as you might
guess, this is the single function that does not itself use the macro definition for
errno). This subterfuge yields a useful feature: because __errno( )is a function, you
can place breakpoints on it while debugging, to determine where a particular error
occurs.
Nevertheless, because the result of the macro errno is the address of the global
variable errno, C programs can set the value of errno in the standard way:
errno = someErrorNumber;
As with any other errno implementation, take care not to have a local variable of
the same name.
In VxWorks, the underlying global errno is a single predefined global variable that
can be referenced directly by application code that is linked with VxWorks (either
statically on the host or dynamically at load time). However, for errno to be useful
in the multitasking environment of VxWorks, each task must see its own version
of errno. Therefore errno is saved and restored by the kernel as part of each tasks
context every time a context switch occurs. Similarly, interrupt service routines (ISRs)
see their own versions of errno.
This is accomplished by saving and restoring errno on the interrupt stack as part
of the interrupt enter and exit code provided automatically by the kernel (see
2.6.1 Connecting Routines to Interrupts, p.66). Thus, regardless of the VxWorks
context, an error code can be stored or consulted with direct manipulation of the
global variable errno.
Almost all VxWorks functions follow a convention that indicates simple success or
failure of their operation by the actual return value of the function. Many functions
return only the status values OK (0) or ERROR (-1). Some functions that normally
return a nonnegative number (for example, open( ) returns a file descriptor) also
return ERROR to indicate an error. Functions that return a pointer usually return
NULL (0) to indicate an error. In most cases, a function returning such an error
indication also sets errno to the specific error code.
The global variable errno is never cleared by VxWorks routines. Thus, its value
always indicates the last error status set. When a VxWorks subroutine gets an error
23
VxWorks 5.5
Programmers Guide
indication from a call to another routine, it usually returns its own error indication
without modifying errno. Thus, the value of errno that is set in the lower-level
routine remains available as the indication of error type.
For example, the VxWorks routine intConnect( ), which connects a user routine to
a hardware interrupt, allocates memory by calling malloc( ) and builds the
interrupt driver in this allocated memory. If malloc( ) fails because insufficient
memory remains in the pool, it sets errno to a code indicating an insufficientmemory error was encountered in the memory allocation library, memLib. The
malloc( ) routine then returns NULL to indicate the failure. The intConnect( )
routine, receiving the NULL from malloc( ), then returns its own error indication of
ERROR. However, it does not alter errno leaving it at the insufficient memory
code set by malloc( ). For example:
if ((pNew = malloc (CHUNK_SIZE)) = = NULL)
return (ERROR);
It is recommended that you use this mechanism in your own subroutines, setting
and examining errno as a debugging technique. A string constant associated with
errno can be displayed using printErrno( ) if the errno value has a corresponding
string entered in the error-status symbol table, statSymTbl. See the reference entry
errnoLib for details on error-status values and building statSymTbl.
VxWorks errno values encode the module that issues the error, in the most
significant two bytes, and uses the least significant two bytes for individual error
numbers. All VxWorks module numbers are in the range 1500; errno values with
a module number of zero are used for source compatibility.
All other errno values (that is, positive values greater than or equal to 501<<16,
and all negative values) are available for application use.
See the reference entry on errnoLib for more information about defining and
decoding errno values with this convention.
24
2
Basic OS
of the task at the point of the exception. The kernel and other tasks continue
uninterrupted. A description of the exception is transmitted to the Tornado
development tools, which can be used to examine the suspended task; see the
Tornado Users Guide: Shell for details.
Tasks can also attach their own handlers for certain hardware exceptions through
the signal facility. If a task has supplied a signal handler for an exception, the
default exception handling described above is not performed. A user-defined
signal handler is useful for recovering from catastrophic events. Typically,
setjmp( ) is called to define the point in the program where control will be restored,
and longjmp( ) is called in the signal handler to restore that context. Note that
longjmp( ) restores the state of the tasks signal mask.
Signals are also used for signaling software exceptions as well as hardware
exceptions. They are described in more detail in 2.3.7 Signals, p.55 and in the
reference entry for sigLib.
25
VxWorks 5.5
Programmers Guide
Figure 2-4
Shared Code
TASKS
taskOne (void)
{
...
myFunc();
...
}
taskTwo (void)
{
myFunc();
...
...
}
SHARED CODE
myFunc (void)
{
...
}
This may or may not be desirable, depending on the nature of the application. For
example, a packet driver can mix streams from different tasks because the packet
header identifies the destination of each packet.
The majority of VxWorks routines use the following reentrancy techniques:
task variables
should only be called once. As a rule, functions should avoid static variables that
keep state information. Init( ) functions are one exception, where using a static
variable that returns the success or failure of the original Init( ) is appropriate.
26
2
Basic OS
Many subroutines are pure code, having no data of their own except dynamic stack
variables. They work exclusively on data provided by the caller as parameters. The
linked-list library, lstLib, is a good example of this. Its routines operate on lists and
nodes provided by the caller in each subroutine call.
Subroutines of this kind are inherently reentrant. Multiple tasks can use such
routines simultaneously, without interfering with each other, because each task
does indeed have its own stack. See Figure 2-5.
Figure 2-5
TASKS
taskOne ( )
{
...
comFunc(1);
...
}
taskTwo ( )
{
...
comFunc(2);
...
}
TASK STACKS
COMMON SUBROUTINE
...
var = 1
...
...
var = 2
...
comFunc (arg)
{
int var = arg;
}
Some libraries encapsulate access to common data. This kind of library requires
some caution because the routines are not inherently reentrant. Multiple tasks
simultaneously invoking the routines in the library might interfere with access to
common variables. Such libraries must be made explicitly reentrant by providing
a mutual-exclusion mechanism to prohibit tasks from simultaneously executing
critical sections of code. The usual mutual-exclusion mechanism is the mutex
semaphore facility provided by semMLib and described in Mutual-Exclusion
Semaphores, p.40.
27
VxWorks 5.5
Programmers Guide
Task Variables
Some routines that can be called by multiple tasks simultaneously may require
global or static variables with a distinct value for each calling task. For example,
several tasks may reference a private buffer of memory and yet refer to it with the
same global variable.
To accommodate this, VxWorks provides a facility called task variables that allows
4-byte variables to be added to a tasks context, so that the value of such a variable
is switched every time a task switch occurs to or from its owner task. Typically,
several tasks declare the same variable (4-byte memory location) as a task variable.
Each of those tasks can then treat that single memory location as its own private
variable; see Figure 2-6. This facility is provided by the routines taskVarAdd( ),
taskVarDelete( ), taskVarSet( ), and taskVarGet( ), which are described in the
reference entry for taskVarLib.
Figure 2-6
OLD TCB
pTaskVar
NEW TCB
pTaskVar
globDat
globDat
value restored
from new
tasks TCB
value saved
in old
tasks TCB
current value of
globDat
Use this mechanism sparingly. Each task variable adds a few microseconds to the
context switching time for its task, because the value of the variable must be saved
and restored as part of the tasks context. Consider collecting all of a modules task
variables into a single dynamically allocated structure, and then making all
accesses to that structure indirectly through a single pointer. This pointer can then
be the task variable for all tasks using that module.
28
2
Basic OS
With VxWorks, it is possible to spawn several tasks with the same main routine.
Each spawn creates a new task with its own stack and context. Each spawn can also
pass the main routine different parameters to the new task. In this case, the same
rules of reentrancy described in Task Variables, p.28 apply to the entire task.
This is useful when the same function needs to be performed concurrently with
different sets of parameters. For example, a routine that monitors a particular kind
of equipment might be spawned several times to monitor several different pieces
of that equipment. The arguments to the main routine could indicate which
particular piece of equipment the task is to monitor.
In Figure 2-7, multiple joints of the mechanical arm use the same code. The tasks
manipulating the joints invoke joint( ). The joint number (jointNum) is used to
indicate which joint on the arm to manipulate.
Figure 2-7
joint_2
joint_3
joint_1
joint
(
int jointNum
)
{
/* joint code here */
}
29
VxWorks 5.5
Programmers Guide
The root task is the first task executed by the kernel. The entry point of the root task
is usrRoot( )in installDir/target/config/all/usrConfig.c and initializes most
VxWorks facilities. It spawns such tasks as the logging task, the exception task, the
network task, and the tRlogind daemon. Normally, the root task terminates and is
deleted after all initialization has occurred.
Logging Task: tLogTask
The log task, tLogTask, is used by VxWorks modules to log system messages
without having to perform I/O in the current task context. For more information,
see 4.5.3 Message Logging, p.122 and the reference entry for logLib.
Exception Task: tExcTask
The exception task, tExcTask, supports the VxWorks exception handling package
by performing functions that cannot occur at interrupt level. It is also used for
actions that cannot be performed in the current tasks context, such as task suicide.
It must have the highest priority in the system. Do not suspend, delete, or change
the priority of this task. For more information, see the reference entry for excLib.
Network Task: tNetTask
The tNetTask daemon handles the task-level functions required by the VxWorks
network. Configure VxWorks with the INCLUDE_NET_LIB component to spawn
the tNetTask task.
Target Agent Task: tWdbTask
The target agent task, tWdbTask, is created if the target agent is set to run in task
mode. It services requests from the Tornado target server; for information about
this server, see the Tornado Users Guide: Overview. Configure VxWorks with the
INCLUDE_WDB component to include the target agent.
Tasks for Optional Components
The following VxWorks system tasks are created if their associated configuration
constants are defined; for more information, see the Tornado Users Guide:
Configuration and Build.
30
2
Basic OS
tShell
If you have included the target shell in the VxWorks configuration, it is
spawned as this task. Any routine or task that is invoked from the target shell,
rather than spawned, runs in the tShell context. For more information, see
6. Target Tools. Configure VxWorks with the INCLUDE_SHELL component to
include the target shell.
tRlogind
If you have included the target shell and the rlogin facility in the VxWorks
configuration, this daemon allows remote users to log in to VxWorks. It
accepts a remote login request from another VxWorks or host system and
spawns tRlogInTask and tRlogOutTask. These tasks exist as long as the
remote user is logged on. During the remote session, the shells (and any other
tasks) input and output are redirected to the remote user. A tty-like interface
is provided to the remote user through the use of the VxWorks pseudoterminal driver, ptyDrv. For more information, see 4.7.1 Serial I/O Devices
(Terminal and Pseudo-Terminal Devices), p.132 and the reference entry for
ptyDrv. Configure VxWorks with the INCLUDE_RLOGIN component to
include the rlogin facility.
tTelnetd
If you have included the target shell and the telnet facility in the VxWorks
configuration, this daemon allows remote users to log in to VxWorks with
telnet. It accepts a remote login request from another VxWorks or host system
and spawns the input task tTelnetInTask and output task tTelnetOutTask.
These tasks exist as long as the remote user is logged on. During the remote
session, the shells (and any other tasks) input and output are redirected to the
remote user. A tty-like interface is provided to the remote user through the use
of the VxWorks pseudo-terminal driver, ptyDrv. See 4.7.1 Serial I/O Devices
(Terminal and Pseudo-Terminal Devices), p.132 and the reference entry for
ptyDrv for further explanation. Configure VxWorks with the
INCLUDE_TELNET component to include the telnet facility.
tPortmapd
If you have included the RPC facility in the VxWorks configuration, this
daemon is an RPC server that acts as a central registrar for RPC servers
running on the same machine. RPC clients query the tPortmapd daemon to
find out how to contact the various servers. Configure VxWorks with the
INCLUDE_RPC component to include the portmap facility.
31
VxWorks 5.5
Programmers Guide
The optional products VxMP and VxFusion provide for intertask communication
between multiple CPUs. See 11. Shared-Memory Objects and 10. Distributed Message
Queues.
TASKS
task 1
MEMORY
access
sharedData
sharedData
access
task 2
sharedData
task 3
sharedData
32
access
2
Basic OS
The most powerful method available for mutual exclusion is the disabling of
interrupts. Such a lock guarantees exclusive access to the CPU:
funcA ()
{
int lock = intLock();
.
.
/* critical region of code that cannot be interrupted */
.
intUnlock (lock);
}
WARNING: Do not call VxWorks system routines with interrupts locked. Violating
33
VxWorks 5.5
Programmers Guide
However, this method can lead to unacceptable real-time response. Tasks of higher
priority are unable to execute until the locking task leaves the critical region, even
though the higher-priority task is not itself involved with the critical region. While
this kind of mutual exclusion is simple, if you use it, make sure to keep the
duration short. A better mechanism is provided by semaphores, discussed in
2.3.3 Semaphores, p.34.
!
WARNING: The critical region code should not block. If it does, preemption could
be re-enabled.
2.3.3 Semaphores
VxWorks semaphores are highly optimized and provide the fastest intertask
communication mechanism in VxWorks. Semaphores are the primary means for
addressing the requirements of both mutual exclusion and task synchronization,
as described below:
There are three types of Wind semaphores, optimized to address different classes
of problems:
binary
The fastest, most general-purpose semaphore. Optimized for
synchronization or mutual exclusion.
34
2
Basic OS
mutual exclusion
A special binary semaphore optimized for problems inherent in mutual
exclusion: priority inheritance, deletion safety, and recursion.
counting
Like the binary semaphore, but keeps track of the number of times a
semaphore is given. Optimized for guarding multiple instances of a
resource.
VxWorks provides not only the Wind semaphores, designed expressly for
VxWorks, but also POSIX semaphores, designed for portability. An alternate
semaphore library provides the POSIX-compatible semaphore interface; see
3.6 POSIX Semaphores, p.85.
The semaphores described here are for use on a single CPU. The optional product
VxMP provides semaphores that can be used across processors; see 11. SharedMemory Objects.
Semaphore Control
Instead of defining a full set of semaphore control routines for each type of
semaphore, the Wind semaphores provide a single uniform interface for
semaphore control. Only the creation routines are specific to the semaphore type.
Table 2-12 lists the semaphore control routines.
Table 2-12
Description
semBCreate( )
semMCreate( )
semCCreate( )
semDelete( )
semTake( )
Takes a semaphore.
semGive( )
Gives a semaphore.
semFlush( )
35
VxWorks 5.5
Programmers Guide
associated memory. Take care when deleting semaphores, particularly those used
for mutual exclusion, to avoid deleting a semaphore that another task still requires.
Do not delete a semaphore unless the same task first succeeds in taking it.
Binary Semaphores
36
2
Basic OS
Figure 2-9
Taking a Semaphore
no
no
semaphore
available?
timeout =
NO_WAIT
yes
yes
task continues;
semaphore
taken
Figure 2-10
task is
pended for
timeout
value
task continues;
semaphore
not taken
Giving a Semaphore
no
semaphore
available?
yes
task continues;
semaphore
remains
unchanged
no
tasks
pended?
task continues,
semaphore
made available
yes
task at front of
queue made ready;
semaphore remains
unavailable
Mutual Exclusion
37
VxWorks 5.5
Programmers Guide
When a task wants to access the resource, it must first take that semaphore. As long
as the task keeps the semaphore, all other tasks seeking access to the resource are
blocked from execution. When the task is finished with the resource, it gives back
the semaphore, allowing another task to use the resource.
Thus, all accesses to a resource requiring mutual exclusion are bracketed with
semTake( ) and semGive( ) pairs:
semTake (semMutex, WAIT_FOREVER);
.
. /* critical region, only accessible by a single task at a time */
.
semGive (semMutex);
Synchronization
38
2
Basic OS
Example 2-1
/* ID of sync semaphore */
init (
int someIntNum
)
{
/* connect interrupt service routine */
intConnect (INUM_TO_IVEC (someIntNum), eventInterruptSvcRout, 0);
/* create semaphore */
syncSem = semBCreate (SEM_Q_FIFO, SEM_EMPTY);
/* spawn task used for synchronization. */
taskSpawn ("sample", 100, 0, 20000, task1, 0,0,0,0,0,0,0,0,0,0);
}
task1 (void)
{
...
semTake (syncSem, WAIT_FOREVER); /* wait for event to occur */
printf ("task 1 got the semaphore\n");
...
/* process event */
}
eventInterruptSvcRout (void)
{
...
semGive (syncSem);
/* let task 1 process event */
...
}
Broadcast synchronization allows all processes that are blocked on the same
semaphore to be unblocked atomically. Correct application behavior often requires
a set of tasks to process an event before any task of the set has the opportunity to
process further events. The routine semFlush( ) addresses this class of
synchronization problem by unblocking all tasks pended on a semaphore.
39
VxWorks 5.5
Programmers Guide
Mutual-Exclusion Semaphores
Priority Inversion
t1
HIGH
t1
priority
Figure 2-11
LOW
t2
t3
t3
t3
time
KEY:
= take semaphore
= preemption
= give semaphore
= priority inheritance/release
= own semaphore
= block
40
2
Basic OS
the time it normally takes t3 to finish with the resource, there would be no problem
because the resource cannot be preempted. However, the low-priority task is
vulnerable to preemption by medium-priority tasks (like t2), which could inhibit
t3 from relinquishing the resource. This condition could persist, blocking t1 for an
indefinite period of time.
The mutual-exclusion semaphore has the option SEM_INVERSION_SAFE, which
enables a priority-inheritance algorithm. The priority-inheritance protocol assures
that a task that holds a resource executes at the priority of the highest-priority task
blocked on that resource. Once the task priority has been elevated, it remains at the
higher level until all mutual-exclusion semaphores that the task holds are released;
then the task returns to its normal, or standard, priority. Hence, the inheriting
task is protected from preemption by any intermediate-priority tasks. This option
must be used in conjunction with a priority queue (SEM_Q_PRIORITY).
Priority Inheritance
t1
HIGH
t3
t1
priority
Figure 2-12
LOW
t2
t3
time
KEY:
= take semaphore
= preemption
= give semaphore
= priority inheritance/release
= own semaphore
= block
41
VxWorks 5.5
Programmers Guide
Deletion Safety
42
2
Basic OS
semGive (mySem);
printf ("funcA: Released mutual-exclusion semaphore\n");
}
funcB ()
{
semTake (mySem, WAIT_FOREVER);
printf ("funcB: Got mutual-exclusion semaphore\n");
...
semGive (mySem);
printf ("funcB: Releases mutual-exclusion semaphore\n");
}
Counting Semaphores
Resulting Behavior
semCCreate( )
semTake( )
Semaphore taken.
43
VxWorks 5.5
Programmers Guide
Table 2-13
Resulting Behavior
semTake( )
Semaphore taken.
semTake( )
Semaphore taken.
semTake( )
semGive( )
semGive( )
Counting semaphores are useful for guarding multiple copies of resources. For
example, the use of five tape drives might be coordinated using a counting
semaphore with an initial count of 5, or a ring buffer with 256 entries might be
implemented using a counting semaphore with an initial count of 256. The initial
count is specified as an argument to the semCCreate( ) routine.
The uniform Wind semaphore interface includes two special options. These
options are not available for the POSIX-compatible semaphores described in
3.6 POSIX Semaphores, p.85.
Timeouts
44
2
Basic OS
Queues
Wind semaphores include the ability to select the queuing mechanism employed
for tasks blocked on a semaphore. They can be queued based on either of two
criteria: first-in first-out (FIFO) order, or priority order; see Figure 2-13.
Figure 2-13
FIFO QUEUE
PRIORITY QUEUE
TCB
TCB
TCB
TCB
200
120
TCB
TCB
90
100
80
TCB
110
140
TCB
priority
110
Priority ordering better preserves the intended priority structure of the system at
the expense of some overhead in semTake( ) in sorting the tasks by priority. A FIFO
queue requires no priority sorting overhead and leads to constant-time
performance. The selection of queue type is specified during semaphore creation
with semBCreate( ), semMCreate( ), or semCCreate( ). Semaphores using the
priority inheritance option (SEM_INVERSION_SAFE) must select priority-order
queuing.
This section describes using VxWorks events with semaphores. You can also use
VxWorks events with other VxWorks objects. For more information, see
2.4 VxWorks Events, p.57.
45
VxWorks 5.5
Programmers Guide
Using Events
The VxWorks events implementation does not propose to keep track of all the
resources a task is currently registered with. Therefore, a resource can attempt to
send events to a task that no longer exists. For example, a task may be deleted or
may self-destruct while still registered with a resource to receive events. This error
is detected only when the resource becomes free, and is reported by having
semGive( ) return ERROR. However, in this case the error does not mean the
semaphore was not given or that the message was not properly delivered. It simply
means the resource could not send events to the registered task. This is a different
behavior from the one presently in place under VxWorks, however it is the same
behavior that exists for pSOS message queues and semaphores.
Performance Impact
46
2
Basic OS
a task. Furthermore, the call may unpend a task waiting for events, which means
the caller may be preempted, even if no task is waiting for the semaphore.
The semDestroy( ) routine performance is impacted in cases where a task is
waiting for events from the semaphore, since the task has to be awakened. Also
note that, in this case, events need not be sent.
message queue 1
message
task 1
task 2
message
message queue 2
Multiple tasks can send to and receive from the same message queue. Full-duplex
communication between two tasks generally requires two message queues, one for
each direction; see Figure 2-14.
47
VxWorks 5.5
Programmers Guide
There are two message-queue subroutine libraries in VxWorks. The first of these,
msgQLib, provides Wind message queues, designed expressly for VxWorks; the
second, mqPxLib, is compatible with the POSIX standard (1003.1b) for real-time
extensions. See 3.5.1 Comparison of POSIX and Wind Scheduling, p.82 for a
discussion of the differences between the two message-queue designs.
Wind message queues are created, used, and deleted with the routines shown in
Table 2-14. This library provides messages that are queued in FIFO order, with a
single exception: there are two priority levels, and messages marked as high
priority are attached to the head of the queue.
Table 2-14
Description
msgQCreate( )
msgQDelete( )
msgQSend( )
msgQReceive( )
48
2
Basic OS
Timeouts
49
VxWorks 5.5
Programmers Guide
== NULL)
return (ERROR);
/* send a normal priority message, blocking if queue is full */
if (msgQSend (myMsgQId, MESSAGE, sizeof (MESSAGE), WAIT_FOREVER,
MSG_PRI_NORMAL) == ERROR)
return (ERROR);
}
The VxWorks show( ) command produces a display of the key message queue
attributes, for either kind of message queue. For example, if myMsgQId is a Wind
message queue, the output is sent to the standard output device, and looks like the
following:
-> show myMsgQId
Message Queue Id
Task Queuing
Message Byte Len
Messages Max
Messages Queued
Receivers Blocked
Send timeouts
Receive timeouts
:
:
:
:
:
:
:
0x3adaf0
FIFO
4
30
14
0
0
: 0
Real-time systems are often structured using a client-server model of tasks. In this
model, server tasks accept requests from client tasks to perform some service, and
usually return a reply. The requests and replies are usually made in the form of
intertask messages. In VxWorks, message queues or pipes (see 2.3.5 Pipes, p.53) are
a natural way to implement this.
For example, client-server communications might be implemented as shown in
Figure 2-15. Each server task creates a message queue to receive request messages
from clients. Each client task creates a message queue to receive reply messages
from servers. Each request message includes a field containing the msgQId of the
clients reply message queue. A server tasks main loop consists of reading
request messages from its request message queue, performing the request, and
sending a reply to the clients reply message queue.
50
2
Basic OS
The same architecture can be achieved with pipes instead of message queues, or by
other means that are tailored to the needs of the particular application.
2
Figure 2-15
reply queue 1
message
client 1
request queue
server task
message
client 2
reply queue 2
message
This section describes using VxWorks events with message queues. You can also
use VxWorks events with other VxWorks objects. For more information, see
2.4 VxWorks Events, p.57.
Using Events
51
VxWorks 5.5
Programmers Guide
Only one task can be registered with a message queue at any given time. The
events a message queue sends to a task can be retrieved by the task using routines
in eventLib. Details on when message queues send events are documented in the
reference entry for msgQEvStart( ).
In some applications, the creator of a message queue may wish to know when a
message queue failed to send events. Such a scenario can occur if a task registers
with a message queue, and is subsequently deleted before having time to
unregister. In this situation, a send operation could cause the message queue to
attempt to send events to the deleted task. Such an attempt would obviously fail.
If the message queue is created with the SG_Q_EVENTSEND_ERROR_NOTIFY
option, the send operation returns an error. Otherwise, VxWorks handles the error
quietly.
Using eventReceive( ), a task may pend on events meant to be sent by a message
queue. If the message queue is deleted, the task pending on events is returned to
the ready state, just like the tasks that may be pending on the message queue itself.
Existing VxWorks API
The VxWorks events implementation does not propose to keep track of all the
resources a task is currently registered with. Therefore, a resource can attempt to
send events to a task that no longer exists. For example, a task may be deleted or
may self-destruct while still registered with a resource to receive events. This error
is detected only when the resource becomes free, and is reported by having
msgQSend( ) return ERROR. However, in this case the error does not mean the
semaphore was not given or that the message was not properly delivered. It simply
means the resource could not send events to the registered task. This is a different
behavior than the one presently in place under VxWorks, however it is the same
behavior that exists for pSOS message queues.
Performance Impact
52
2
Basic OS
2.3.5 Pipes
Pipes provide an alternative interface to the message queue facility that goes
through the VxWorks I/O system. Pipes are virtual I/O devices managed by the
driver pipeDrv. The routine pipeDevCreate( ) creates a pipe device and the
underlying message queue associated with that pipe. The call specifies the name
of the created pipe, the maximum number of messages that can be queued to it,
and the maximum length of each message:
status = pipeDevCreate ("/pipe/name", max_msgs, max_length);
The created pipe is a normally named I/O device. Tasks can use the standard I/O
routines to open, read, and write pipes, and invoke ioctl routines. As they do with
other I/O devices, tasks block when they read from an empty pipe until data is
available, and block when they write to a full pipe until there is space available.
Like message queues, ISRs can write to a pipe, but cannot read from a pipe.
As I/O devices, pipes provide one important feature that message queues
cannotthe ability to be used with select( ). This routine allows a task to wait for
data to be available on any of a set of I/O devices. The select( ) routine also works
with other asynchronous I/O devices including network sockets and serial
devices. Thus, by using select( ), a task can wait for data on a combination of
several pipes, sockets, and serial devices; see 4.3.8 Pending on Multiple File
Descriptors: The Select Facility, p.117.
Pipes allow you to implement a client-server model of intertask communications;
see Servers and Clients with Message Queues, p.50.
53
VxWorks 5.5
Programmers Guide
a reliable byte-stream to flow between them in each direction as in a circuit. For this
reason, TCP is often referred to as a virtual circuit protocol.
UDP provides a simpler but less robust form of communication. In UDP
communications, data is sent between sockets in separate, unconnected,
individually addressed packets called datagrams. A process creates a datagram
socket and binds it to a particular port. There is no notion of a UDP connection.
Any UDP socket, on any host in the network, can send messages to any other UDP
socket by specifying its Internet address and port number.
One of the biggest advantages of socket communications is that it is
homogeneous. Socket communications among processes are exactly the same
regardless of the location of the processes in the network, or the operating system
under which they are running. Processes can communicate within a single CPU,
across a backplane, across an Ethernet, or across any connected combination of
networks. Socket communications can occur between VxWorks tasks and host
system processes in any combination. In all cases, the communications look
identical to the application, except, of course, for their speed.
For more information, see VxWorks Network Programmers Guide: Networking APIs
and the reference entry for sockLib.
Remote Procedure Calls (RPC) is a facility that allows a process on one machine to
call a procedure that is executed by another process on either the same machine or
a remote machine. Internally, RPC uses sockets as the underlying communication
mechanism. Thus with RPC, VxWorks tasks and host system processes can invoke
routines that execute on other VxWorks or host machines, in any combination.
As discussed in the previous sections on message queues and pipes, many realtime systems are structured with a client-server model of tasks. In this model,
client tasks request services of server tasks, and then wait for their reply. RPC
formalizes this model and provides a standard protocol for passing requests and
returning replies. Also, RPC includes tools to help generate the client interface
routines and the server skeleton.
For more information about RPC, see VxWorks Network Programmers Guide: RPC,
Remote Procedure Calls.
54
2
Basic OS
2.3.7 Signals
VxWorks supports a software signal facility. Signals asynchronously alter the
control flow of a task. Any task or ISR can raise a signal for a particular task. The
task being signaled immediately suspends its current thread of execution and
executes the task-specified signal handler routine the next time it is scheduled to
run. The signal handler executes in the receiving tasks context and makes use of
that tasks stack. The signal handler is invoked even if the task is blocked.
Signals are more appropriate for error and exception handling than as a generalpurpose intertask communication mechanism. In general, signal handlers should
be treated like ISRs; no routine should be called from a signal handler that might
cause the handler to block. Because signals are asynchronous, it is difficult to
predict which resources might be unavailable when a particular signal is raised. To
be perfectly safe, call only those routines that can safely be called from an ISR (see
Table 2-21). Deviate from this practice only when you are sure your signal handler
cannot create a deadlock situation.
The wind kernel supports two types of signal interface: UNIX BSD-style signals
and POSIX-compatible signals. The POSIX-compatible signal interface, in turn,
includes both the fundamental signaling interface specified in the POSIX standard
1003.1, and the queued-signals extension from POSIX 1003.1b. For more
information, see 3.9 POSIX Queued Signals, p.105. For the sake of simplicity, we
recommend that you use only one interface type in a given application, rather than
mixing routines from different interfaces.
For more information about signals, see the reference entry for sigLib.
NOTE: The VxWorks implementation of sigLib does not impose any special
restrictions on operations on SIGKILL, SIGCONT, and SIGSTOP signals such as
those imposed by UNIX. For example, the UNIX implementation of signal( )
cannot be called on SIGKILL and SIGSTOP.
55
VxWorks 5.5
Programmers Guide
Table 2-15
UNIX BSD
Compatible
Call
Description
signal( )
signal( )
kill( )
kill( )
raise( )
N/A
sigaction( )
sigvec( )
sigsuspend( )
pause( )
sigpending( )
N/A
sigemptyset( )
sigfillset( )
sigaddset( )
sigdelset( )
sigismember( )
sigsetmask( )
sigprocmask( )
sigsetmask( )
sigprocmask( )
sigblock( )
In many ways, signals are analogous to hardware interrupts. The basic signal
facility provides a set of 31 distinct signals. A signal handler binds to a particular
signal with sigvec( ) or sigaction( ) in much the same way that an ISR is connected
to an interrupt vector with intConnect( ). A signal can be asserted by calling kill( ).
This is analogous to the occurrence of an interrupt. The routines sigsetmask( ) and
sigblock( ) or sigprocmask( ) let signals be selectively inhibited.
Certain signals are associated with hardware exceptions. For example, bus errors,
illegal instructions, and floating-point exceptions raise specific signals.
Signal Configuration
56
2
Basic OS
57
VxWorks 5.5
Programmers Guide
WARNING: Because events are not, and cannot be, reserved, two independent
applications can attempt to use the same events on the same task. As a precaution,
middleware applications using VxWorks events should always publish a list of the
events they are using.
In the pSOS operating system, events can be sent from a resource to a task, from an
ISR to a task, or directly between two tasks. Tasks, ISRs, and resources all use the
same ev_send( ) API to send events.
For a task to receive events from a resource, the task must register with that
resource and request it to send a specific set of events when it becomes free. The
resource is either a semaphore or a message queue. When the resource becomes
free, it sends the set of events to the registered task. This task may, or may not be,
waiting for the events.
As mentioned above, a task can also receive events from another task. For example,
if two tasks agree to send events between them, taskA could send taskB a specific
events set when it (taskA) finishes executing, to let taskB know that this has
occurred. As with events sent from a resource, the receiving task may, or may not
be, waiting for the events.
A task can wait for multiple events from one or more sources. Each source can send
multiple events, and a task can also wait to receive only one event, or all events.
For example, a task may be waiting for events 1 to 10, where 1 to 4 come from a
semaphore, 5 to 6 come from a message queue, and 7 to 10 come from another task.
A task can also specify a timeout when waiting for events.
58
2
Basic OS
Only one task can register itself to receive events from a resource. If another task
subsequently registers with the same resource, the previously registered task is
automatically unregistered, without its knowledge. This behavior differs in
VxWorks events. For details, see VxWorks Enhancements to pSOS Events, p.61.
When a task registers with a resource, events are not sent immediately, even if the
resource is free at the time of registration. The events are sent the next time the
resource becomes free. For example, a semaphore will send events the next time it
is given, as long as no task is waiting for it. (Being given does not always mean that
a resource is free; see Freeing Resources, p.59). This behavior is configurable for
VxWorks events. For details, see VxWorks Enhancements to pSOS Events, p.61.
Freeing Resources
When a resource sends events to a task to indicate that it is free, it does not mean
that resource is reserved. Therefore, a task waiting for events from a resource will
unpend when the resource becomes free, however the resource may be taken in the
meantime. There are no guarantees that the resource will still be available, if the
task subsequently attempts to take ownership of it.
As mentioned above, a resource only sends events when it becomes free. This is not
synonymous with being released. To clarify, if a semaphore is given, it is actually
being released. However, it is not considered free if another task is waiting for it at
the time it is released. Therefore, in cases where two or more tasks are constantly
exchanging ownership of a resource, that resource never becomes free; thus, it may
never send events.
Meaning
ev_send( )
ev_receive( )
sm_notify( )
59
VxWorks 5.5
Programmers Guide
Table 2-16
Meaning
q_notify( )
q_vnotify( )
60
2
Basic OS
When VxWorks events were implemented, some enhancements were made to the
basic pSOS functionality. This section describes those enhancements and
configuration options, and compares the resulting behavior of VxWorks events
with pSOS events.
In order to solve this problem, VxWorks events provide an option whereby the
second task is not allowed to register with the resource, if another task is
already registered with it. If a second task tries to register with the resource, an
error is returned. In VxWorks, you can configure the registration mechanism
to use either the VxWorks or the pSOS behavior.
Option for Immediate Send. As mentioned in Registering for Events, p.59, when
a pSOS task registers with a resource, the resource does not send events to the
task immediately, even if it is free at the time of registration. For VxWorks
events, the default behavior is the same. However, VxWorks events provide an
option that allows a task, at the time of registration, to request that the resource
send the events immediately, if the resource is free at the time of registration.
Option for Automatic Unregister. There are situations in which a task may want
to receive events from a resource only once, and then unregister. The pSOS
implementation requires a task to explicitly unregister after having received
events from the resource. The VxWorks implementation provides an option
whereby a registering task can tell the resource to only send events once, and
automatically unregister the task when this occurs.
61
VxWorks 5.5
Programmers Guide
Each task has its own events field or container, referred to as the task events register.
The task events register is a per task 32-bit field used to store the events that a task
receives from resources, ISRs, and other tasks.
You do not access the task events register directly. Tasks, ISRs, and resources fill the
events register of a particular task by sending events to that task. A task can also
send itself events, thereby filling its own events register. Events 25 to 32 (VXEV25
or 0x01000000 to VXEV32 or 0x80000000) are reserved for system use only, and are
not available to VxWorks users. Table 2-17 describes the routines that affect the
contents of the events register.
Table 2-17
Effects
eventReceive( )
eventClear( )
eventSend( )
semGive( )
Copies events into the event register, if a task is registered with the
semaphore.
msgQSend( )
Copies events into the event register, if a task is registered with the
message queue.
For details on the API for VxWorks events, see the reference entries for eventLib,
semEvLib, and msgQEvLib.
Show Routines
For the purpose of debugging systems that make use of events, the taskShow,
semShow, and msgQShow libraries display event information.
62
2
Basic OS
Comparison of Events
VxWorks Routine
pSOS Routine
Comments
eventSend
ev_send
Direct port
eventReceive
ev_receive
Direct port
New functionality in VxWorks.
eventClear
semEvStart
sm_notify
semEvStop
sm_notify
msgQEvStart
q_vnotify
63
VxWorks 5.5
Programmers Guide
Table 2-18
Comparison of Events
VxWorks Routine
pSOS Routine
Comments
msgQEvStop
q_vnotify
q_notify
Description
wdCreate( )
wdDelete( )
wdStart( )
wdCancel( )
A watchdog timer is first created by calling wdCreate( ). Then the timer can be
started by calling wdStart( ), which takes as arguments the number of ticks to
delay, the C function to call, and an argument to be passed to that function. After
64
2
Basic OS
the specified number of ticks have elapsed, the function is called with the specified
argument. The watchdog timer can be canceled any time before the delay has
elapsed by calling wdCancel( ).
Example 2-4
Watchdog Timers
/* Creates a watchdog timer and sets it to go off in 3 seconds.*/
/* includes */
#include "vxWorks.h"
#include "logLib.h"
#include "wdLib.h"
/* defines */
#define SECONDS (3)
WDOG_ID myWatchDogId;
task (void)
{
/* Create watchdog */
if ((myWatchDogId = wdCreate( )) == NULL)
return (ERROR);
/* Set timer to go off in SECONDS - printing a message to stdout */
if (wdStart (myWatchDogId, sysClkRateGet( ) * SECONDS, logMsg,
"Watchdog timer just expired\n") == ERROR)
return (ERROR);
/* ... */
}
65
VxWorks 5.5
Programmers Guide
For boards with an MMU, the optional product VxVMI provides write protection
for the interrupt vector table; see 12. Virtual Memory Interface.
Table 2-20
Interrupt Routines
Call
Description
intConnect( )
intContext( )
intCount( )
intLevelSet( )
intLock( )
Disables interrupts.
intUnlock( )
Re-enables interrupts.
intVecBaseSet( )
intVecBaseGet( )
intVecSet( )
intVecGet( )
66
2
Basic OS
Figure 2-16
For target boards with VME backplanes, the BSP provides two standard routines
for controlling VME bus interrupts, sysIntEnable( ) and sysIntDisable( ).
67
VxWorks 5.5
Programmers Guide
Routines
bLib
All routines
errnoLib
errnoGet( ), errnoSet( )
fppArchLib
fppSave( ), fppRestore( )
intLib
intArchLib
intLock( ), intUnlock( )
logLib
logMsg( )
lstLib
mathALib
msgQLib
msgQSend( )
pipeDrv
write( )
rngLib
selectLib
selWakeup( ), selWakeupAll( )
semLib
sigLib
kill( )
taskLib
tickLib
tyLib
tyIRd( ), tyITx( )
vxLib
vxTas( ), vxMemProbe( )
wdLib
wdStart( ), wdCancel( )
68
2
Basic OS
For this reason, the basic restriction on ISRs is that they must not invoke routines
that might cause the caller to block. For example, they must not try to take a
semaphore, because if the semaphore is unavailable, the kernel tries to switch the
caller to the pended state. However, ISRs can give semaphores, releasing any tasks
waiting on them.
Because the memory facilities malloc( ) and free( ) take a semaphore, they cannot
be called by ISRs, and neither can routines that make calls to malloc( ) and free( ).
For example, ISRs cannot call any creation or deletion routines.
ISRs also must not perform I/O through VxWorks drivers. Although there are no
inherent restrictions in the I/O system, most device drivers require a task context
because they might block the caller to wait for the device. An important exception
is the VxWorks pipe driver, which is designed to permit writes by ISRs.
VxWorks supplies a logging facility, in which a logging task prints text messages
to the system console. This mechanism was specifically designed for ISR use, and
is the most common way to print messages from ISRs. For more information, see
the reference entry for logLib.
An ISR also must not call routines that use a floating-point coprocessor. In
VxWorks, the interrupt driver code created by intConnect( ) does not save and
restore floating-point registers; thus, ISRs must not include floating-point
instructions. If an ISR requires floating-point instructions, it must explicitly save
and restore the registers of the floating-point coprocessor using routines in
fppArchLib.
All VxWorks utility libraries, such as the linked-list and ring-buffer libraries, can
be used by ISRs. As discussed earlier (2.2.6 Task Error Status: errno, p.22), the global
variable errno is saved and restored as a part of the interrupt enter and exit code
generated by the intConnect( ) facility. Thus, errno can be referenced and modified
by ISRs as in any other code. Table 2-21 lists routines that can be called from ISRs.
69
VxWorks 5.5
Programmers Guide
in the boot ROMs re-displays the exception description; see Tornado Users Guide:
Setup and Startup.
One example of such an exception is the following message:
workQPanic: Kernel work queue overflow.
This exception usually occurs when kernel calls are made from interrupt level at a
very high rate. It generally indicates a problem with clearing the interrupt signal
or a similar driver problem.
CAUTION: Some hardware prevents masking certain interrupt levels; check the
The ISR cannot use any VxWorks operating system facilities that depend on
interrupt locks for correct operation. The effective result is that the ISR cannot
safely make any call to any VxWorks function, except reboot.
For more information, see the VxWorks architecture supplement document for the
architecture in question.
70
2
Basic OS
WARNING: The use of NMI with any VxWorks functionality, other than reboot, is
not recommended. Routines marked as interrupt safe do not imply they are NMI
safe and, in fact, are usually the very ones that NMI routines must not call (because
they typically use intLock( ) to achieve the interrupt safe condition).
Shared Memory and Ring Buffers. ISRs can share variables, buffers, and ring
semaphores and VxMP shared semaphores) that tasks can take and wait for.
Message Queues. ISRs can send messages to message queues for tasks to
receive (except for shared message queues using VxMP). If the queue is full,
the message is discarded.
Pipes. ISRs can write messages to pipes that tasks can read. Tasks and ISRs can
write to the same pipes. However, if the pipe is full, the message written is
discarded because the ISR cannot block. ISRs must not invoke any I/O routine
on pipes other than write( ).
signal handlers.
71
VxWorks 5.5
Programmers Guide
72
3
POSIX Standard Interfaces
3.1 Introduction
The POSIX standard for real-time extensions (1003.1b) specifies a set of interfaces
to kernel facilities. To improve application portability, the VxWorks kernel, wind,
includes both POSIX interfaces and interfaces designed specifically for VxWorks.
This chapter uses the qualifier Wind to identify facilities designed expressly for
use with the VxWorks wind kernel. For example, you can find a discussion of Wind
semaphores contrasted to POSIX semaphores in 3.6.1 Comparison of POSIX and
Wind Semaphores, p.86.
POSIX asynchronous Input/Output (AIO) routines are available in the aioPxLib
library. The VxWorks AIO implementation meets the specification in the POSIX
1003.1b standard. For more information, see 4.6 Asynchronous Input/Output, p.123.
73
VxWorks 5.5
Programmers Guide
The system-wide real-time clock is identified in the clock and timer routines as
CLOCK_REALTIME, and is defined in time.h. VxWorks provides routines to access
the system-wide real-time clock. For more information, see the reference entry for
clockLib.
The POSIX timer facility provides routines for tasks to signal themselves at some
time in the future. Routines are provided to create, set, and delete a timer. For more
information, see the reference entry for timerLib. When a timer goes off, the
default signal, SIGALRM, is sent to the task. To install a signal handler that
executes when the timer expires, use the sigaction( ) routine (see 2.3.7 Signals,
p.55).
Example 3-1
POSIX Timers
/* This example creates a new timer and stores it in timerid. */
/* includes */
#include "vxWorks.h"
#include "time.h"
int createTimer (void)
{
timer_t timerid;
/* create timer */
if (timer_create (CLOCK_REALTIME, NULL, &timerid) == ERROR)
{
printf ("create FAILED\n");
return (ERROR);
}
return (OK);
}
74
3
POSIX Standard Interfaces
impose severe and unpredictable delays in execution time, paging and swapping
are undesirable in real-time systems. Consequently, the wind kernel never uses
them.
However, the POSIX 1003.1b standard for real-time extensions is also used with
operating systems that do perform paging and swapping. On such systems,
applications that attempt real-time performance can use the POSIX page-locking
facilities to protect certain blocks of memory from paging and swapping.
To facilitate porting programs between other POSIX-conforming systems and
VxWorks, VxWorks includes the POSIX page-locking routines. The routines have
no adverse affect in VxWorks systems, because all memory is essentially always
locked.
The POSIX page-locking routines are part of the memory management library,
mmanPxLib, and are listed in Table 3-1. When used in VxWorks, these routines do
nothing except return a value of OK (0), since all pages are always kept in memory.
Table 3-1
mlockall( )
munlockall( )
mlock( )
munlock( )
75
VxWorks 5.5
Programmers Guide
Stack Size
The stacksize attribute specifies the size of the stack to be used. This value can be
rounded up to a page boundary.
Default Value: Uses the default stack size set for taskLib.
Stack Address
The stackaddr attribute specifies the base of a region of user allocated memory to
be used as a stack region for the created thread. Because the default value is NULL,
the system should allocate a stack for the thread when it is created.
Detach State
The detachstate attribute describes the state of a thread. With POSIX threads, the
creator of a thread can block until the thread exits (see the entries for
pthread_exit( ) and pthread_join( ) in the VxWorks API Reference). In this case, the
thread is a joinable thread. Otherwise, it is a detached thread. A thread that was
76
3
POSIX Standard Interfaces
PTHREAD_CREATE_JOINABLE
Contention Scope
Inherit Scheduling
77
VxWorks 5.5
Programmers Guide
Scheduling Policy
The schedpolicy attribute describes the scheduling policy for the thread, and is
valid only if the value of the inheritsched attribute is
PTHREAD_EXPLICIT_SCHED.
Note that because the default value for the inheritsched attribute is
PTHREAD_INHERIT_SCHED, the schedpolicy attribute is not used by default. For
more information, see 3.5.3 Getting and Displaying the Current Scheduling Policy,
p.84.
Scheduling Parameters
The schedparam attribute describes the scheduling parameters for the thread, and
is valid only if the value of the inheritsched attribute is
PTHREAD_EXPLICIT_SCHED.
more information, see 3.5.2 Getting and Setting POSIX Task Priorities, p.82.
78
3
POSIX Standard Interfaces
Following are examples of creating a thread using the default attributes and using
explicit attributes.
Example 3-2
Example 3-3
Example 3-4
79
VxWorks 5.5
Programmers Guide
Meaning
pthread_setcancelstate( )
pthread_setcanceltype( )
pthread_cleanup_push( )
pthread_cleanup_pop( )
80
3
POSIX Standard Interfaces
Description
sched_setparam( )
sched_getparam( )
sched_setscheduler( )
sched_yield( )
sched_getscheduler( )
sched_get_priority_max( )
sched_get_priority_min( )
sched_rr_get_interval( )
81
VxWorks 5.5
Programmers Guide
The POSIX standard uses the term FIFO scheduling. VxWorks documentation
uses the term preemptive priority scheduling. Only the terms differ; both
describe the same priority-based policy.
The POSIX priority numbering scheme is the inverse of the Wind scheme. In
POSIX, the higher the number, the higher the priority; in the Wind scheme, the
lower the number, the higher the priority, where 0 is the highest priority.
Accordingly, the priority numbers used with the POSIX scheduling library,
schedPxLib, do not match those used and reported by all other components of
VxWorks. You can override this default by setting the global variable
posixPriorityNumbering to FALSE. If you do this, schedPxLib uses the Wind
numbering scheme (smaller number = higher priority) and its priority
numbers match those used by the other components of VxWorks.
82
3
POSIX Standard Interfaces
83
VxWorks 5.5
Programmers Guide
84
3
POSIX Standard Interfaces
Example 3-7
2. Some host operating systems, such as UNIX, require symbolic names for objects that are to
be shared among processes. This is because processes do not normally share memory in
such operating systems. In VxWorks, there is no requirement for named semaphores,
because all kernel objects have unique identifiers. However, using named semaphores of the
POSIX variety provides a convenient way of determining the objects ID.
85
VxWorks 5.5
Programmers Guide
Table 3-4
Description
semPxLibInit( )
sem_init( )
sem_destroy( )
sem_open( )
sem_close( )
sem_unlink( )
sem_wait( )
Lock a semaphore.
sem_trywait( )
sem_post( )
Unlock a semaphore.
sem_getvalue( )
priority inheritance
task-deletion safety
semaphore timeouts
86
3
POSIX Standard Interfaces
The POSIX terms wait (or lock) and post (or unlock) correspond to the VxWorks
terms take and give, respectively. The POSIX routines for locking, unlocking, and
getting the value of semaphores are used for both named and unnamed
semaphores.
The routines sem_init( ) and sem_destroy( ) are used for initializing and
destroying unnamed semaphores only. The sem_destroy( ) call terminates an
unnamed semaphore and deallocates all associated memory.
The routines sem_open( ), sem_unlink( ), and sem_close( ) are for opening and
closing (destroying) named semaphores only. The combination of sem_close( ) and
sem_unlink( ) has the same effect for named semaphores as sem_destroy( ) does
for unnamed semaphores. That is, it terminates the semaphore and deallocates the
associated memory.
!
87
VxWorks 5.5
Programmers Guide
88
3
POSIX Standard Interfaces
void syncTask
(
sem_t * pSem
)
{
/* wait for synchronization from unnameSem */
if (sem_wait (pSem) == -1)
{
printf ("syncTask: sem_wait failed \n");
return;
}
else
printf ("syncTask:sem locked; doing synced action...\n");
/* do something useful here */
}
Create the semaphore if it does not already exist (if it exists, either fail
or open the semaphore, depending on whether O_EXCL is specified).
O_EXCL
Open the semaphore only if newly created; fail if the semaphore exists.
The results, based on the flags and whether the semaphore accessed already exists,
are shown in Table 3-5. There is no entry for O_EXCL alone, because using that flag
alone is not meaningful.
Table 3-5
If Semaphore Exists
None
Semaphore is opened.
Routine fails.
O_CREAT
Semaphore is opened.
Semaphore is created.
Routine fails.
Semaphore is created.
89
VxWorks 5.5
Programmers Guide
The output is sent to the standard output device, and provides information about
the POSIX semaphore mySem with two tasks blocked waiting for it:
Semaphore name
sem_open() count
Semaphore value
No. of blocked tasks
:mySem
:3
:0
:2
For a group of collaborating tasks to use a named semaphore, one of the tasks first
creates and initializes the semaphore, by calling sem_open( ) with the O_CREAT
flag. Any task that needs to use the semaphore thereafter, opens it by calling
sem_open( ) with the same name (but without setting O_CREAT). Any task that
has opened the semaphore can use it by locking it with sem_wait( ) (blocking) or
sem_trywait( ) (non-blocking) and unlocking it with sem_post( ).
To remove a semaphore, all tasks using it must first close it with sem_close( ), and
one of the tasks must also unlink it. Unlinking a semaphore with sem_unlink( )
removes the semaphore name from the name table. After the name is removed
from the name table, tasks that currently have the semaphore open can still use it,
but no new tasks can open this semaphore. The next time a task tries to open the
semaphore without the O_CREAT flag, the operation fails. The semaphore vanishes
when the last task closes it.
Example 3-9
3. This is not a POSIX routine, nor is it designed for use from programs; use it from the
Tornado shell (see the Tornado Users Guide: Shell for details).
90
3
POSIX Standard Interfaces
/* includes */
#include "vxWorks.h"
#include "semaphore.h"
#include "fcntl.h"
/* forward declaration */
int syncSemTask (char * name);
int nameSem
(
char * name
)
{
sem_t * semId;
/* create a named semaphore, initialize to 0*/
printf ("nameSem: creating semaphore\n");
if ((semId = sem_open (name, O_CREAT, 0, 0)) == (sem_t *) -1)
{
printf ("nameSem: sem_open failed\n");
return;
}
printf ("nameSem: spawning sync task\n");
taskSpawn ("tSyncSemTask", 90, 0, 2000, syncSemTask, name);
/* do something useful to synchronize with syncSemTask */
/* give semaphore */
printf ("nameSem: posting semaphore - synchronizing action\n");
if (sem_post (semId) == -1)
{
printf ("nameSem: sem_post failed\n");
return;
}
/* all done */
if (sem_close (semId) == -1)
{
printf ("nameSem: sem_close failed\n");
return;
}
if (sem_unlink (name) == -1)
{
printf ("nameSem: sem_unlink failed\n");
return;
}
printf ("nameSem: closed and unlinked semaphore\n");
}
91
VxWorks 5.5
Programmers Guide
int syncSemTask
(
char * name
)
{
sem_t * semId;
/* open semaphore */
printf ("syncSemTask: opening semaphore\n");
if ((semId = sem_open (name, 0)) == (sem_t *) -1)
{
printf ("syncSemTask: sem_open failed\n");
return;
}
/* block waiting for synchronization from nameSem */
printf ("syncSemTask: attempting to take semaphore...\n");
if (sem_wait (semId) == -1)
{
printf ("syncSemTask: taking sem failed\n");
return;
}
printf ("syncSemTask: has semaphore, doing synced action ...\n");
/* do something useful here */
if (sem_close (semId) == -1)
{
printf ("syncSemTask: sem_close failed\n");
return;
}
}
92
3
POSIX Standard Interfaces
Protocol
The protocol mutex attribute describes how the mutex deals with the priority
inversion problem described in the section for mutual-exclusion semaphores
(Mutual-Exclusion Semaphores, p.40).
The prioceiling attribute is the POSIX priority ceiling for a mutex created with the
protocol attribute set to PTHREAD_PRIO_PROTECT.
Note that the POSIX priority numbering scheme is the inverse of the Wind scheme.
See 3.5.1 Comparison of POSIX and Wind Scheduling, p.82.
A priority ceiling is defined by the following conditions:
Any thread attempting to acquire a mutex, whose priority is higher than the
ceiling, cannot acquire the mutex.
Any thread whose priority is lower than the ceiling value has its priority
elevated to the ceiling value for the duration that the mutex is held.
The threads priority is restored to its previous value when the mutex is
released.
93
VxWorks 5.5
Programmers Guide
Description
mqPxLibInit( )
mq_open( )
mq_close( )
mq_unlink( )
mq_send( )
mq_receive( )
mq_notify( )
mq_setattr( )
mq_getattr( )
To configure VxWorks to include the POSIX message queue routine, include the
INCLUDE_POSIX_MQ component. The initialization routine mqPxLibInit( )
makes the POSIX message queue routines available, and is called automatically
when the INCLUDE_POSIX_MQ component is included in the system.
32
FIFO or priority-based
Priority-based
94
3
POSIX Standard Interfaces
Table 3-7
Optional
Not available
Task Notification
Not available
Close/Unlink Semantics
No
Yes
POSIX message queues are also portable, if you are migrating to VxWorks from
another 1003.1b-compliant system. This means that you can use POSIX message
queues without having to change the code, thereby reducing the porting effort.
Tasks can set or clear the O_NONBLOCK flag (but not the other attributes) using
mq_setattr( ), and get the values of all the attributes using mq_getattr( ).
Example 3-10
16
int attrEx
(
char * name
)
{
mqd_t
mqPXId;
/* mq descriptor */
95
VxWorks 5.5
Programmers Guide
struct mq_attr
struct mq_attr
char
int
attr;
oldAttr;
buffer[MSG_SIZE];
prio;
96
3
POSIX Standard Interfaces
The output is sent to the standard output device, and looks like the following:
Message queue name
No. of messages in queue
Maximum no. of messages
Maximum message size
: MyQueue
: 1
: 16
: 16
:
:
:
:
:
:
:
0x3adaf0
FIFO
4
30
14
0
0
: 0
NOTE: The built-in show( ) routine handles Wind message queues; see the Tornado
Users Guide: Shell for information on built-in routines. You can also use the
Tornado browser to get information on Wind message queues; see the Tornado
Users Guide: Browser for details.
97
VxWorks 5.5
Programmers Guide
98
3
POSIX Standard Interfaces
/* defines */
#define MQ_NAME "exampleMessageQueue"
/* forward declarations */
void receiveTask (void);
void sendTask (void);
31
16
99
VxWorks 5.5
Programmers Guide
return;
}
else
{
printf ("receiveTask: Msg of priority %d received:\n\t\t%s\n",
prio, msg);
}
}
/* sendTask.c - mq sending example */
/* includes */
#include "vxWorks.h"
#include "mqueue.h"
#include "fcntl.h"
#include "mqEx.h"
/* defines */
#define MSG
"greetings"
#define HI_PRIO 30
void sendTask (void)
{
mqd_t
mqPXId;
100
3
POSIX Standard Interfaces
extension to signaling, which allows you, for example, to carry a queue identifier
with the signal (see 3.9 POSIX Queued Signals, p.105).
The mq_notify( ) mechanism is designed to alert the task only for new messages
that are actually available. If the message queue already contains messages, no
notification is sent when more messages arrive. If there is another task that is
blocked on the queue with mq_receive( ), that other task unblocks, and no
notification is sent to the task registered with mq_notify( ).
Notification is exclusive to a single task: each queue can register only one task for
notification at a time. Once a queue has a task to notify, no attempts to register with
mq_notify( ) can succeed until the notification request is satisfied or cancelled.
Once a queue sends notification to a task, the notification request is satisfied, and
the queue has no further special relationship with that particular task; that is, the
queue sends a notification signal only once per mq_notify( ) request. To arrange
for one particular task to continue receiving notification signals, the best approach
is to call mq_notify( ) from the same signal handler that receives the notification
signals. This reinstalls the notification request as soon as possible.
To cancel a notification request, specify NULL instead of a notification signal. Only
the currently registered task can cancel its notification request.
Example 3-12
"PxQ1"
64
/* forward declarations */
static void exNotificationHandle (int, siginfo_t *, void *);
static void exMqRead (mqd_t);
/*
* exMqNotify - example of how to use mq_notify()
*
* This routine illustrates the use of mq_notify() to request notification
101
VxWorks 5.5
Programmers Guide
attr;
sigNotify;
mySigAction;
exMqId
/*
/*
/*
/*
/* make nonblocking */
102
3
POSIX Standard Interfaces
/*
* Set up notification: fill in a sigevent structure and pass it
* to mq_notify(). The queue ID is passed as an argument to the
* signal handler.
*/
sigNotify.sigev_signo
= SIGUSR1;
sigNotify.sigev_notify
= SIGEV_SIGNAL;
sigNotify.sigev_value.sival_int = (int) exMqId;
if (mq_notify (exMqId, &sigNotify) == -1)
{
printf ("mq_notify failed\n");
return (-1);
}
/*
* We just created the message queue, but it may not be empty;
* a higher-priority task may have placed a message there while
* we were requesting notification. mq_notify() does nothing if
* messages are already in the queue; therefore we try to
* retrieve any messages already in the queue.
*/
exMqRead (exMqId);
/*
* Now we know the queue is empty, so we will receive a signal
* the next time a message arrives.
*
* We send a message, which causes the notify handler to be invoked.
* It is a little silly to have the task that gets the notification
* be the one that puts the messages on the queue, but we do it here
* to simplify the example. A real application would do other work
* instead at this point.
*/
if (mq_send (exMqId, pMess, 1 + strlen (pMess), 0) == -1)
{
printf ("mq_send failed\n");
return (-1);
}
/* Cleanup */
if (mq_close (exMqId) == -1)
{
printf ("mq_close failed\n");
return (-1);
}
/* More cleanup */
if (mq_unlink (QNAM) == -1)
{
printf ("mq_unlink failed\n");
return (-1);
}
103
VxWorks 5.5
Programmers Guide
return (0);
}
/*
* exNotificationHandle - handler to read in messages
*
* This routine is a signal handler; it reads in messages from a
* message queue.
*/
static void exNotificationHandle
(
int
sig,
/* signal number */
siginfo_t * pInfo,
/* signal information */
void *
pSigContext
/* unused (required by posix) */
)
{
struct sigevent
sigNotify;
mqd_t
exMqId;
/* Get the ID of the message queue out of the siginfo structure. */
exMqId = (mqd_t) pInfo->si_value.sival_int;
/*
* Request notification again; it resets each time
* a notification signal goes out.
*/
sigNotify.sigev_signo = pInfo->si_signo;
sigNotify.sigev_value = pInfo->si_value;
sigNotify.sigev_notify = SIGEV_SIGNAL;
if (mq_notify (exMqId, &sigNotify) == -1)
{
printf ("mq_notify failed\n");
return;
}
/* Read in the messages */
exMqRead (exMqId);
}
/*
* exMqRead - read in messages
*
* This small utility routine receives and displays all messages
* currently in a POSIX message queue; assumes queue has O_NONBLOCK.
*/
static void exMqRead
(
mqd_t
exMqId
)
104
3
POSIX Standard Interfaces
{
char
int
msg[MSG_SIZE];
prio;
/*
* Read in the messages - uses a loop to read in the messages
* because a notification is sent ONLY when a message is sent on
* an EMPTY message queue. There could be multiple msgs if, for
* example, a higher-priority task was sending them. Because the
* message queue was opened with the O_NONBLOCK flag, eventually
* this loop exits with errno set to EAGAIN (meaning we did an
* mq_receive() on an empty message queue).
*/
while (mq_receive (exMqId, msg, MSG_SIZE, &prio) != -1)
{
printf ("exMqRead: received message: %s\n",msg);
}
if (errno != EAGAIN)
{
printf ("mq_receive: errno = %d\n", errno);
}
}
sigqueue( ) enables the queueing of multiple signals for any task. The kill( )
routine, by contrast, delivers only a single signal, even if multiple signals
arrive before the handler runs.
105
VxWorks 5.5
Programmers Guide
to the third reserved signal number). All signals delivered with sigqueue( ) are
queued by numeric order, with lower-numbered signals queuing ahead of highernumbered signals.
POSIX 1003.1b also introduced an alternative means of receiving signals. The
routine sigwaitinfo( ) differs from sigsuspend( ) or pause( ) in that it allows your
application to respond to a signal without going through the mechanism of a
registered signal handler: when a signal is available, sigwaitinfo( ) returns the
value of that signal as a result, and does not invoke a signal handler even if one is
registered. The routine sigtimedwait( ) is similar, except that it can time out.
For detailed information on signals, see the reference entry for sigLib.
Table 3-8
Description
sigqueue( )
sigwaitinfo( )
sigtimedwait( )
106
4
I/O System
4.1 Introduction
The VxWorks I/O system is designed to present a simple, uniform,
device-independent interface to any kind of device, including:
The VxWorks I/O system provides standard C libraries for both basic and buffered
I/O. The basic I/O libraries are UNIX-compatible; the buffered I/O libraries are
ANSI C-compatible. Internally, the VxWorks I/O system has a unique design that
makes it faster and more flexible than most other I/O systems. These are important
attributes in a real-time system.
This chapter first describes the nature of files and devices, and the user view of basic
and buffered I/O. The next section discusses the details of some specific devices.
The third section is a detailed discussion of the internal structure of the VxWorks
I/O system. The final sections describe PCMCIA and PCI support.
The diagram in Figure 4-1 illustrates the relationships between the different
elements of the VxWorks I/O system. All of these elements are discussed in this
chapter, except for file system routines (which are dealt with in 5. Local File
Systems), and the network elements (which are covered in the VxWorks Network
Programmers Guide).
107
VxWorks 5.5
Programmers Guide
Figure 4-1
Application
Buffered I/O: stdio
fioLib
fread( )
fwrite( )
fioRead( )
printf( )
sprintf( )
CBIO Routines
NetWork Stack
Routines
xxRead( )
xxWrite( )
xxRead( )
xxWrite( )
bsdSend( )
bsdReceive( )
Network Sockets
Interface
send( )
recv( )
read( )
write( )
Driver Routines
xxRead( )
xxWrite( )
Hardware Devices
Network
Disk Drive
Serial Device
Library Routines
tyLib
NOTE: In Figure 4-1, the dotted arrow line indicates that the CBIO block device is
108
4
I/O System
109
VxWorks 5.5
Programmers Guide
If a matching device name cannot be found, then the I/O function is directed at a
default device. You can set this default device to be any device in the system,
including no device at all, in which case failure to match a device name returns an
error. You can obtain the current default path by using ioDefPathGet( ). You can
set the default path by using ioDefPathSet( ).
Non-block devices are named when they are added to the I/O system, usually at
system initialization time. Block devices are named when they are initialized for
use with a specific file system. The VxWorks I/O system imposes no restrictions on
the names given to devices. The I/O system does not interpret device or filenames
in any way, other than during the search for matching device and filenames.
It is useful to adopt some naming conventions for device and filenames: most
device names begin with a slash (/), except non-NFS network devices and
VxWorks DOS devices (dosFs).
By convention, NFS-based network devices are mounted with names that begin
with a slash. For example:
/usr
Non-NFS network devices are named with the remote machine name followed by
a colon. For example:
host:
The remainder of the name is the filename in the remote directory on the remote
system.
File system devices using dosFs are often named with uppercase letters and/or
digits followed by a colon. For example:
DEV1:
NOTE: Filenames and directory names on dosFs devices are often separated by
backslashes (\). These can be used interchangeably with forward slashes (/).
!
CAUTION: Because device names are recognized by the I/O system using simple
substring matching, a slash (/) should not be used alone as a device name.
110
4
I/O System
Description
creat( )
Creates a file.
delete( )
Deletes a file.
open( )
close( )
Closes a file.
read( )
write( )
ioctl( )
111
VxWorks 5.5
Programmers Guide
Global Redirection
When VxWorks is initialized, the global assignments of the standard fds are
directed, by default, to the system console. When tasks are spawned, they initially
have no task-specific fd assignments; instead, they use the global assignments.
The global assignments can be redirected using ioGlobalStdSet( ). The parameters
to this routine are the global standard fd to be redirected, and the fd to direct it to.
For example, the following call sets global standard output (fd = 1) to be the open
file with a file descriptor of fileFd:
ioGlobalStdSet (1, fileFd);
All tasks in the system that do not have their own task-specific redirection write
standard output to that file thereafter. For example, the task tRlogind calls
ioGlobalStdSet( ) to redirect I/O across the network during an rlogin session.
Task-Specific Redirection
The assignments for a specific task can be redirected using the routine
ioTaskStdSet( ). The parameters to this routine are the task ID (0 = self) of the task
with the assignments to be redirected, the standard fd to be redirected, and the fd
112
4
I/O System
to direct it to. For example, a task can make the following call to write standard
output to fileFd:
ioTaskStdSet (0, 1, fileFd);
All other tasks are unaffected by this redirection, and subsequent global
redirections of standard output do not affect this task.
Hex Value
Description
O_RDONLY
O_WRONLY
O_RDWR
O_CREAT
200
O_TRUNC
400
WARNING: While the third parameter to open( ) is usually optional for other
operating systems, it is required for the VxWorks implementation of open( ). When
the third parameter is not appropriate for any given call, it should be set to zero.
Note that this can be an issue when porting software from UNIX to VxWorks.
113
VxWorks 5.5
Programmers Guide
The mode parameter is used in the following special cases to specify the mode
(permission bits) of a file or to create subdirectories:
In general, you can open only preexisting devices and files with open( ).
However, with NFS network and dosFs devices, you can also create files with
open( ) by ORing O_CREAT with one of the access flags. For NFS devices,
open( ) requires the third parameter specifying the mode of the file:
fd = open ("name", O_CREAT | O_RDWR, 0644);
With both dosFs and NFS devices, you can use the O_CREAT option to create
a subdirectory by setting mode to FSTAT_DIR. Other uses of the mode
parameter with dosFs devices are ignored.
The open( ) routine, if successful, returns a file descriptor (fd). This fd is then used
in subsequent I/O calls to specify that file. The fd is a global identifier that is not task
specific. One task can open a file, and then any other tasks can use the resulting fd
(for example, pipes). The fd remains valid until close( ) is invoked with that fd:
close (fd);
At that point, I/O to the file is flushed (completely written out) and the fd can no
longer be used by any task. However, the same fd number can again be assigned
by the I/O system in any subsequent open( ).
When a task exits or is deleted, the files opened by that task are not automatically
closed unless the task owns the files, because fds are not task-specific by default.
Thus, it is recommended that tasks explicitly close all files when they are no longer
required. As stated previously, there is a limit to the number of files that can be
open at one time.
114
4
I/O System
The read( ) routine waits for input to be available from the specified file, and
returns the number of bytes actually read. For file-system devices, if the number of
bytes read is less than the number requested, a subsequent read( ) returns 0 (zero),
indicating end-of-file. For non-file-system devices, the number of bytes read can be
less than the number requested even if more bytes are available; a subsequent
read( ) may or may not return 0. In the case of serial devices and TCP sockets,
repeated calls to read( ) are sometimes necessary to read a specific number of bytes.
(See the reference entry for fioRead( ) in fioLib). A return value of ERROR (-1)
indicates an unsuccessful read.
The arguments to write( ) are the fd, the address of the buffer that contains the data
to be output, and the number of bytes to be written:
actualBytes = write (fd, &buffer, nBytes);
The write( ) routine ensures that all specified data is at least queued for output
before returning to the caller, though the data may not yet have been written to the
device (this is driver dependent). write( ) returns the number of bytes written; if
the number returned is not equal to the number requested, an error has occurred.
115
VxWorks 5.5
Programmers Guide
If it succeeds in truncating the file, ftruncate( ) returns OK. If the size specified is
larger than the actual size of the file, or if the fd refers to a device that cannot be
truncated, ftruncate( ) returns ERROR, and sets errno to EINVAL.
The ftruncate( ) routine is part of the POSIX 1003.1b standard, but this
implementation is only partially POSIX-compliant: creation and modification
times may not be updated. This call is supported only by dosFsLib, the
DOS-compatible file system library. The routine is provided by the
INCLUDE_POSIX_FTRUNCATE component.
For example, the following call uses the FIOBAUDRATE function to set the baud
rate of a tty device to 9600:
status = ioctl (fd, FIOBAUDRATE, 9600);
The discussion of specific devices in 4.7 Devices in VxWorks, p.131 summarizes the
ioctl( ) functions available for each device. The ioctl( ) control codes are defined in
ioLib.h. For more information, see the reference entries for specific device drivers
or file systems.
116
4
I/O System
Select Macros
Macro
Function
FD_ZERO
FD_SET
FD_CLR
FD_ISSET
117
VxWorks 5.5
Programmers Guide
Applications can use select( ) with any character I/O devices that provide support
for this facility (for example, pipes, serial devices, and sockets). For information on
writing a device driver that supports select( ), see Implementing select( ), p.168.
Example 4-1
MAX_FDS 2
MAX_DATA 1024
PIPEHI
"/pipe/highPriority"
PIPENORM "/pipe/normalPriority"
/************************************************************************
* selServer - reads data as it becomes available from two different pipes
*
* Opens two pipe fds, reading from whichever becomes available. The
* server code assumes the pipes have been created from either another
* task or the shell. To test this code from the shell do the following:
* -> ld < selServer.o
* -> pipeDevCreate ("/pipe/highPriority", 5, 1024)
* -> pipeDevCreate ("/pipe/normalPriority", 5, 1024)
* -> fdHi = open
("/pipe/highPriority", 1, 0)
* -> fdNorm = open ("/pipe/normalPriority", 1, 0)
* -> iosFdShow
* -> sp selServer
* -> i
* At this point you should see selServers state as pended. You can now
* write to either pipe to make the selServer display your message.
* -> write fdNorm, "Howdy", 6
* -> write fdHi, "Urgent", 7
*/
STATUS selServer (void)
{
struct fd_set readFds;
int
fds[MAX_FDS];
int
width;
int
i;
char
buffer[MAX_DATA];
118
/*
/*
/*
/*
/*
4
I/O System
119
VxWorks 5.5
Programmers Guide
To make this type of I/O more efficient and flexible, the stdio package implements
a buffering scheme in which data is read and written in large chunks and buffered
privately. This buffering is transparent to the application; it is handled
automatically by the stdio routines and macros. To access a file with stdio, a file is
opened with fopen( ) instead of open( ) (many stdio calls begin with the letter f):
fp = fopen ("/usr/foo", "r");
120
4
I/O System
The returned value, a file pointer (or fp) is a handle for the opened file and its
associated buffers and pointers. An fp is actually a pointer to the associated data
structure of type FILE (that is, it is declared as FILE *). By contrast, the low-level I/O
routines identify a file with a file descriptor (fd), which is a small integer. In fact, the
FILE structure pointed to by the fp contains the underlying fd of the open file.
An already open fd can be associated belatedly with a FILE buffer by calling
fdopen( ):
fp = fdopen (fd, "r");
After a file is opened with fopen( ), data can be read with fread( ), or a character at
a time with getc( ), and data can be written with fwrite( ), or a character at a time
with putc( ).
The routines and macros to get data into or out of a file are extremely efficient. They
access the buffer with direct pointers that are incremented as data is read or written
by the user. They pause to call the low-level read or write routines only when a
read buffer is empty or a write buffer is full.
!
WARNING: The stdio buffers and pointers are private to a particular task. They are
121
VxWorks 5.5
Programmers Guide
122
4
I/O System
123
VxWorks 5.5
Programmers Guide
Table 4-4
Description
aioPxLibInit( )
aioShow( )
aio_read( )
aio_write( )
aio_listio( )
aio_error( )
aio_return( )
aio_cancel( )
aio_suspend( )
* This function is not built into the Tornado shell. To use it from the Tornado shell,
VxWorks must be configured with the INCLUDE_POSIX_AIO_SHOW
component. When you invoke the function, its output is sent to the standard
output device.
The routine aioSysInit( ) takes three parameters: the number of AIO system tasks
to spawn, and the priority and stack size for these system tasks. The number of
AIO system tasks spawned equals the number of AIO requests that can be handled
in parallel. The default initialization call uses three constants:
MAX_AIO_SYS_TASKS, AIO_TASK_PRIORITY, and AIO_TASK_STACK_SIZE.
When any of the parameters passed to aioSysInit( ) is 0, the corresponding value
is taken from AIO_IO_TASKS_DFLT, AIO_IO_PRIO_DFLT, and
AIO_IO_STACK_DFLT (all defined in installDir/target/h/aioSysDrv.h).
Table 4-5 lists the names of the constants, and shows the constants used within
initialization routines when the parameters are left at their default values of 0, and
where these constants are defined.
Table 4-5
Initialization
Function
aioPxLibInit( ) MAX_LIO_CALLS
AIO_CLUST_MAX
aioSysInit( )
MAX_AIO_SYS_TASKS
AIO_IO_TASKS_DFLT
h/aioSysDrv.h
AIO_TASK_PRIORITY
AIO_IO_PRIO_DFLT
50
h/aioSysDrv.h
124
100
h/private/aioPxLibP.h
4
I/O System
Table 4-5
Initialization
Function
AIO_IO_STACK_DFLT
0x7000 h/aioSysDrv.h
4
125
VxWorks 5.5
Programmers Guide
For full definitions and important additional information, see the reference entry
for aioPxLib.
!
CAUTION: If a routine allocates stack space for the aiocb, that routine must call
The following code uses a pipe for the asynchronous I/O operations. The example
creates the pipe, submits an AIO read request, verifies that the read request is still
in progress, and submits an AIO write request. Under normal circumstances, a
synchronous read to an empty pipe blocks and the task does not execute the write,
but in the case of AIO, we initiate the read request and continue. After the write
request is submitted, the example task loops, checking the status of the AIO
requests periodically until both the read and write complete. Because the AIO
control blocks are on the stack, we must call aio_return( ) before returning from
aioExample( ).
126
4
I/O System
Example 4-2
Asynchronous I/O
/* aioEx.c - example code for using asynchronous I/O */
/* includes */
#include "vxWorks.h"
#include "aio.h"
#include "errno.h"
/* defines */
#define BUFFER_SIZE 200
/************************************************************************
* aioExample - use AIO library
* This example shows the basic functions of the AIO library.
* RETURNS: OK if successful, otherwise ERROR.
*/
STATUS aioExample (void)
{
int
fd;
static char
exFile [] = "/pipe/1stPipe";
struct aiocb aiocb_read; /* read aiocb */
struct aiocb aiocb_write; /* write aiocb */
static char * test_string = "testing 1 2 3";
char
buffer [BUFFER_SIZE]; /* buffer for read aiocb */
pipeDevCreate (exFile, 50, 100);
if ((fd = open (exFile, O_CREAT | O_TRUNC | O_RDWR, 0666)) ==
ERROR)
{
printf ("aioExample: cannot open %s errno 0x%x\n", exFile, errno);
return (ERROR);
}
printf ("aioExample: Example file = %s\tFile descriptor = %d\n",
exFile, fd);
/* initialize read and write aiocbs */
bzero ((char *) &aiocb_read, sizeof (struct aiocb));
bzero ((char *) buffer, sizeof (buffer));
aiocb_read.aio_fildes = fd;
aiocb_read.aio_buf = buffer;
aiocb_read.aio_nbytes = BUFFER_SIZE;
aiocb_read.aio_reqprio = 0;
bzero ((char *) &aiocb_write, sizeof (struct aiocb));
aiocb_write.aio_fildes = fd;
aiocb_write.aio_buf = test_string;
aiocb_write.aio_nbytes = strlen (test_string);
aiocb_write.aio_reqprio = 0;
127
VxWorks 5.5
Programmers Guide
A task can determine whether an AIO request is complete in any of the following
ways:
Use aio_suspend( ) to suspend the task until the AIO request is complete.
128
4
I/O System
Example 4-3
/* defines */
#define BUFFER_SIZE
200
#define LIST_SIZE
1
#define EXAMPLE_SIG_NO 25 /* signal number */
/* forward declarations */
void mySigHandler (int sig, struct siginfo * info, void * pContext);
/************************************************************************
* aioExampleSig - use AIO library.
*
* This example shows the basic functions of the AIO library.
* Note if this is run from the shell it must be spawned. Use:
* -> sp aioExampleSig
*
* RETURNS: OK if successful, otherwise ERROR.
*/
STATUS aioExampleSig (void)
{
int
fd;
static char
exFile [] = "/pipe/1stPipe";
struct aiocb
aiocb_read;
/* read aiocb */
static struct aiocb aiocb_write;
/* write aiocb */
struct sigaction
action;
/* signal info */
static char *
test_string = "testing 1 2 3";
char
buffer [BUFFER_SIZE];
/* aiocb read buffer */
pipeDevCreate (exFile, 50, 100);
if ((fd = open (exFile, O_CREAT | O_TRUNC| O_RDWR, 0666)) == ERROR)
{
printf ("aioExample: cannot open %s errno 0x%x\n", exFile, errno);
return (ERROR);
}
printf ("aioExampleSig: Example file = %s\tFile descriptor = %d\n",
exFile, fd);
/* set up signal handler for EXAMPLE_SIG_NO */
129
VxWorks 5.5
Programmers Guide
action.sa_sigaction = mySigHandler;
action.sa_flags = SA_SIGINFO;
sigemptyset (&action.sa_mask);
sigaction (EXAMPLE_SIG_NO, &action, NULL);
/* initialize read and write aiocbs */
bzero ((char *) &aiocb_read, sizeof (struct aiocb));
bzero ((char *) buffer, sizeof (buffer));
aiocb_read.aio_fildes = fd;
aiocb_read.aio_buf = buffer;
aiocb_read.aio_nbytes = BUFFER_SIZE;
aiocb_read.aio_reqprio = 0;
bzero ((char *) &aiocb_write, sizeof (struct aiocb));
aiocb_write.aio_fildes = fd;
aiocb_write.aio_buf = test_string;
aiocb_write.aio_nbytes = strlen (test_string);
aiocb_write.aio_reqprio = 0;
/* set up signal info */
aiocb_write.aio_sigevent.sigev_signo = EXAMPLE_SIG_NO;
aiocb_write.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
aiocb_write.aio_sigevent.sigev_value.sival_ptr =
(void *) &aiocb_write;
/* initiate the read */
if (aio_read (&aiocb_read) == -1)
printf ("aioExampleSig: aio_read failed\n");
/* verify that it is in progress */
if (aio_error (&aiocb_read) == EINPROGRESS)
printf ("aioExampleSig: read is still in progress\n");
/* write to pipe - the read should be able to complete */
printf ("aioExampleSig: getting ready to initiate the write\n");
if (aio_write (&aiocb_write) == -1)
printf ("aioExampleSig: aio_write failed\n");
/* clean up */
if (aio_return (&aiocb_read) == -1)
printf ("aioExampleSig: aio_return for aiocb_read failed\n");
else
printf ("aioExampleSig: aio read message = %s\n",
aiocb_read.aio_buf);
close (fd);
return (OK);
}
130
4
I/O System
void mySigHandler
(
int
sig,
struct siginfo * info,
void *
pContext
)
{
/* print out what was read */
printf ("mySigHandler: Got signal for aio write\n");
/* write is complete so lets do cleanup for it here */
if (aio_return (info->si_value.sival_ptr) == -1)
{
printf ("mySigHandler: aio_return for aiocb_write failed\n");
printErrno (0);
}
}
Driver Description
ttyDrv
Terminal driver
ptyDrv
Pseudo-terminal driver
pipeDrv
Pipe driver
memDrv
nfsDrv
netDrv
ramDrv
scsiLib
131
VxWorks 5.5
Programmers Guide
tty Options
The tty devices have a full range of options that affect the behavior of the device.
These options are selected by setting bits in the device option word using the
ioctl( ) routine with the FIOSETOPTIONS function (see I/O Control Functions,
p.135). For example, to set all the tty options except OPT_MON_TRAP:
status = ioctl (fd, FIOSETOPTIONS, OPT_TERMINAL & ~OPT_MON_TRAP);
Table 4-7 is a summary of the available options. The listed names are defined in the
header file ioLib.h. For more detailed information, see the reference entry for
tyLib.
Table 4-7
Tty Options
Library
Description
OPT_LINE
Selects line mode. (See Raw Mode and Line Mode, p.133.)
OPT_ECHO
OPT_CRMOD
OPT_TANDEM
OPT_7_BIT
132
4
I/O System
Table 4-7
Description
OPT_MON_TRAP
OPT_ABORT
OPT_TERMINAL
OPT_RAW
A tty device operates in one of two modes: raw mode (unbuffered) or line mode. Raw
mode is the default. Line mode is selected by the OPT_LINE bit of the device option
word (see tty Options, p.132).
In raw mode, each input character is available to readers as soon as it is input from
the device. Reading from a tty device in raw mode causes as many characters as
possible to be extracted from the input ring, up to the limit of the users read buffer.
Input cannot be modified except as directed by other tty option bits.
In line mode, all input characters are saved until a NEWLINE character is input; then
the entire line of characters, including the NEWLINE, is made available in the ring
at one time. Reading from a tty device in line mode causes characters up to the end
of the next line to be extracted from the input ring, up to the limit of the users read
buffer. Input can be modified by the special characters CTRL+H (backspace),
CTRL+U (line-delete), and CTRL+D (end-of-file), which are discussed in Tty Special
Characters, p.133.
The following special characters are enabled if the tty device operates in line mode,
that is, with the OPT_LINE bit set:
The line-delete character, by default CTRL+U, deletes all the characters of the
current line.
133
VxWorks 5.5
Programmers Guide
The end-of-file (EOF) character, by default CTRL+D, causes the current line to
become available in the input ring without a NEWLINE and without entering
the EOF character itself. Thus if the EOF character is the first character typed
on a line, reading that line returns a zero byte count, which is the usual
indication of end-of-file.
The following characters have special effects if the tty device is operating with the
corresponding option bit set:
The software flow control characters CTRL+Q and CTRL+S (XON and XOFF).
Receipt of a CTRL+S input character suspends output to that channel.
Subsequent receipt of a CTRL+Q resumes the output. Conversely, when the
VxWorks input buffer is almost full, a CTRL+S is output to signal the other side
to suspend transmission. When the input buffer is empty enough, a CTRL+Q
is output to signal the other side to resume transmission. The software flow
control characters are enabled by OPT_TANDEM.
The ROM monitor trap character, by default CTRL+X. This character traps to the
ROM-resident monitor program. Note that this is drastic. All normal VxWorks
functioning is suspended, and the computer system is controlled entirely by
the monitor. Depending on the particular monitor, it may or may not be
possible to restart VxWorks from the point of interruption.1 The monitor trap
character is enabled by OPT_MON_TRAP.
The special target shell abort character, by default CTRL+C. This character
restarts the target shell if it gets stuck in an unfriendly routine, such as one that
has taken an unavailable semaphore or is caught in an infinite loop. The target
shell abort character is enabled by OPT_ABORT.
The characters for most of these functions can be changed using the tyLib routines
shown in Table 4-8.
Table 4-8
Description
Modifier
CTRL+H
tyBackspaceSet( )
CTRL+U
line delete
tyDeleteLineSet( )
CTRL+D
tyEOFSet( )
CTRL+C
tyAbortSet( )
1. It will not be possible to restart VxWorks if un-handled external interrupts occur during the
boot countdown.
134
4
I/O System
Table 4-8
Description
Modifier
CTRL+X
tyMonitorTrapSet( )
CTRL+S
output suspend
N/A
CTRL+Q
output resume
N/A
The tty devices respond to the ioctl( ) functions in Table 4-9, defined in ioLib.h. For
more information, see the reference entries for tyLib, ttyDrv, and ioctl( ).
Table 4-9
Description
FIOBAUDRATE
FIOCANCEL
FIOFLUSH
FIOGETNAME
FIOGETOPTIONS
FIONREAD
FIONWRITE
FIOSETOPTIONS
CAUTION: To change the drivers hardware options (for example, the number of
stop bits or parity bits), use the ioctl( ) function SIO_HW_OPTS_SET. Because this
command is not implemented in most drivers, you may need to add it to your BSP
serial driver, which resides in installDir/target/src/drv/sio. The details of how to
implement this command depend on your boards serial chip. The constants
defined in the header file installDir/target/h/sioLib.h provide the POSIX
definitions for setting the hardware options.
135
VxWorks 5.5
Programmers Guide
Creating Pipes
The new pipe can have at most maxMsgs messages queued at a time. Tasks that
write to a pipe that already has the maximum number of messages queued are
blocked until a message is dequeued. Each message in the pipe can be at most
maxLength bytes long; attempts to write longer messages result in an error.
VxWorks pipes are designed to allow ISRs to write to pipes in the same way as
task-level code. Many VxWorks facilities cannot be used from ISRs, including
output to devices other than pipes. However, ISRs can use pipes to communicate
with tasks, which can then invoke such facilities. ISRs write to a pipe using the
write( ) call. Tasks and ISRs can write to the same pipes. However, if the pipe is full,
the message is discarded because the ISRs cannot pend. ISRs must not invoke any
I/O function on pipes other than write( ). For more informationon ISRs, see
2.6 Interrupt Service Code: ISRs, p.65.
Pipe devices respond to the ioctl( ) functions summarized in Table 4-10. The
functions listed are defined in the header file ioLib.h. For more information, see
the reference entries for pipeDrv and for ioctl( ) in ioLib.
Table 4-10
Description
FIOFLUSH
136
4
I/O System
Table 4-10
Description
FIOGETNAME
FIONMSGS
FIONREAD
The driver is initialized automatically by the system with memDrv( ) when the
INCLUDE_USR_MEMDRV component is included in the VxWorks kernel domain.
The call for device creation must be made from the kernel domain:
STATUS memDevCreate
(char * name, char * base, int length)
Memory for the device is an absolute memory location beginning at base. The
length parameter indicates the size of the memory. For additional information on
the memory driver, see the entries for memDrv( ), memDevCreate( ), and
memDev CreateDir( ) in the VxWorks API Reference, as well as the entry for the
host tool memdrvbuild in the Tornado Tools section of the online Tornado Tools
Reference.
The memory driver responds to the ioctl( ) functions summarized in Table 4-11.
The functions listed are defined in the header file ioLib.h.
137
VxWorks 5.5
Programmers Guide
Table 4-11
Description
FIOSEEK
FIOWHERE
For more information, see the reference entries for memDrv and for ioctl( ) in
ioLib.
Access to a remote NFS file system is established by mounting that file system
locally and creating an I/O device for it using nfsMount( ). Its arguments are
(1) the host name of the NFS server, (2) the name of the host file system, and (3) the
local name for the file system.
For example, the following call mounts /usr of the host mars as /vxusr locally:
nfsMount ("mars", "/usr", "/vxusr");
This creates a VxWorks I/O device with the specified local name (/vxusr, in this
example). If the local name is specified as NULL, the local name is the same as the
remote name.
After a remote file system is mounted, the files are accessed as though the file
system were local. Thus, after the previous example, opening the file /vxusr/foo
opens the file /usr/foo on the host mars.
138
4
I/O System
The remote file system must be exported by the system on which it actually resides.
However, NFS servers can export only local file systems. Use the appropriate
command on the server to see which file systems are local. NFS requires
authentication parameters to identify the user making the remote access. To set
these parameters, use the routines nfsAuthUnixSet( ) and nfsAuthUnixPrompt( ).
To include NFS client support, use the INCLUDE_NFS component.
The subject of exporting and mounting NFS file systems and authenticating access
permissions is discussed in more detail in VxWorks Network Programmers Guide:
File Access Applications. See also the reference entries nfsLib and nfsDrv, and the
NFS documentation from Sun Microsystems.
NFS client devices respond to the ioctl( ) functions summarized in Table 4-12. The
functions listed are defined in ioLib.h. For more information, see the reference
entries for nfsDrv and for ioctl( ) in ioLib.
Table 4-12
Description
FIOFSTATGET
FIOGETNAME
FIONREAD
FIOREADDIR
FIOSEEK
FIOSYNC
FIOWHERE
139
VxWorks 5.5
Programmers Guide
write operations are performed on the memory-resident copy of the file. When
closed, the file is copied back to the original remote file if it was modified.
In general, NFS devices are preferable to RSH and FTP devices for performance
and flexibility, because NFS does not copy the entire file into local memory.
However, NFS is not supported by all host systems.
To access files on a remote host using either RSH or FTP, a network device must
first be created by calling the routine netDevCreate( ). The arguments to
netDevCreate( ) are (1) the name of the device, (2) the name of the host the device
accesses, and (3) which protocol to use: 0 (RSH) or 1 (FTP).
For example, the following call creates an RSH device called mars: that accesses the
host mars. By convention, the name for a network device is the remote machines
name followed by a colon (:).
netDevCreate ("mars:", "mars", 0);
RSH and FTP devices respond to the same ioctl( ) functions as NFS devices except
for FIOSYNC and FIOREADDIR. The functions are defined in the header file
ioLib.h. For more information, see the reference entries for netDrv and ioctl( ).
140
4
I/O System
The CBIO disk cache is designed to make rotational media disk I/O more efficient,
as well as to automatically detect changes in disks.
Disk I/O Efficiency
The disk cache reduces the number of disk accesses and accelerates disk read and
write operations by means of the following techniques:
Most Recently Used (MRU) disk blocks are stored in RAM, which results in the
most frequently accessed data being retrieved from memory rather than from
disk.
Reading data from disk is performed in large units, relying on the read-ahead
feature, one of the disk caches tunable parameters.
Write operations are optimized because they occur to memory first. Then,
updating the disk happens in an orderly manner, by delayed write, another
tunable parameter.
Overall, the main performance advantage arises from a dramatic reduction in the
amount of time spent seeking by the disk drive, thus maximizing the time
141
VxWorks 5.5
Programmers Guide
available for the disk to read and write actual data. In other words, you get efficient
use of the disk drives available throughput.
The disk cache offers a number of operational parameters that can be tuned by the
user to suit a particular file system workload pattern, for example, delayed write,
read ahead, and bypass threshold.
The technique of delaying writes to disk means that if the system is turned off
unexpectedly, updates that have not yet been written to the disk are lost. To
minimize the effect of a possible crash, the disk cache periodically updates the
disk. Modified blocks of data are not kept in memory more than a specified period
of time.
By specifying a small update period, the possible worst-case loss of data from a
crash is the sum of changes possible during that specified period. For example, it
is assumed that an update period of two seconds is sufficiently large to effectively
optimize disk writes, yet small enough to make the potential loss of data a
reasonably minor concern. It is possible to set the update period to zero, in which
case, all updates are flushed to disk immediately.
The disk cache allows you to negotiate between disk performance and memory
consumption: The more memory allocated to the disk cache, the higher the hit
ratio observed, which means increasingly better performance of file system
operations.
Bypass Threshold
Another tunable parameter is the bypass threshold, which defines how much data
constitutes a request large enough to justify bypassing the disk cache.
When significantly large read or write requests are made by the application, the
disk cache is circumvented and there is a direct transfer of data between the disk
controller and the user data buffer. The use of bypassing, in conjunction with
support for contiguous file allocation and access (the FIOCONTIG ioctl( )
command and the DOS_O_CONTIG open( ) flag), should provide performance
equivalent to that offered by the raw file system rawFs.
For details on all of the tunable parameters associated with the disk cache, see the
entry for dcacheDevTune( ) in the VxWorks API Reference. For complete details on
the disk cache, see the entry for dcacheCbio.
Detection of Disk Changes
The disk cache component also provides for automatic detection of disk changes.
The detection process is based on two assumptions: one about the uniqueness of a
disk, and a second about the time required to change a disk.
142
4
I/O System
The first assumption is that the first blockthat is, the boot block of each cartridge
or disketteis unique. This is typically the case because most media is
pre-formatted during manufacturing, and the boot block contains a 32-bit volume
serial ID that is set by the manufacturer as unique. The formatting utility in dosFs
preserves the volume ID when one exists on the volume being formatted; when no
valid ID is found on the disk it creates a new one based on the time that the
formatting takes place.
The second assumption is that it takes at least two seconds to physically replace a
disk. If a disk has been inactive for two seconds or longer, its boot block is verified
against a previously stored boot block signature. If they do not match, the file
system module is notified of the change, and in turn un-mounts the volume,
marking all previously open file descriptors as obsolete. A new volume is then
mounted, if a disk is in the drive. This can trigger an optional automatic
consistency check that detects any structural inconsistencies resulting from a
previous disk removal in the midst of a disk update (see the entry for
dosFsDevCreate( ) in the VxWorks API Reference).
!
CAUTION: The disk cache is designed to benefit rotational media devices and
VxWorks 5.5
Programmers Guide
drive, and then associate a logical device name with each partition. If you insert
removable media having fewer partitions than the maximum number defined,
then only the number of partitions configured on the removable media are
accessible. Additional partitions previously defined and named cannot be
accessed.
In some applications it is desirable to use a file system to organize and access data
although no disk or other traditional media is present. The CBIO RAM disk facility
allows the use of a file system to access data stored in RAM memory. RAM disks
can be created using volatile as well a non-volatile RAM.
NOTE: The ramDiskCbio library implements a RAM disk using the CBIO
interface; the ramDrv library implements a RAM disk using the BLK_DEV
interface.
For more information, see the ramDiskCbio entries in the VxWorks API Reference.
Description
CBIO_RESET
CBIO_STATUS_CHK
CBIO_DEVICE_LOCK
CBIO_DEVICE_UNLOCK
CBIO_DEVICE_EJECT
CBIO_CACHE_FLUSH
CBIO_CACHE_INVAL
CBIO_CACHE_NEWBLK
144
4
I/O System
For use with block devices, VxWorks is supplied with file system libraries
compatible with the MS-DOS file systems. There are also libraries for a simple raw
disk file system, for SCSI tape devices, for CD-ROM devices, and for flash memory
devices. Use of these file systems is discussed in 5. Local File Systems in this manual
(which discusses the Target Server File System as well). Also see the entries for
dosFsLib, rawFsLib, tapeFsLib, cdromFsLib, and tffsDrv in the VxWorks API
Reference.
RAM drivers, as implemented in ramDrv, emulate disk devices but actually keep
all data in memory. Memory location and disk size are specified when a RAM
device is created by calling ramDevCreate( ). This routine can be called repeatedly
to create multiple RAM disks.
Memory for the RAM disk can be pre-allocated and the address passed to
ramDevCreate( ), or memory can be automatically allocated from the system
memory pool using malloc( ).
After the device is created, a name and file system (for example, dosFs or rawFs)
must be associated with it using the file systems device creation routine and
format routine; for example, dosFsDevCreate( ) and dosFsVolFormat( ).
Information describing the device is passed to the file system in a BLK_DEV
structure. A pointer to this structure is returned by the RAM disk creation routine.
145
VxWorks 5.5
Programmers Guide
If the RAM disk memory already contains a disk image, the first argument to
ramDevCreate( ) is the address in memory, and the formatting arguments must be
identical to those used when the image was created. For example:
pBlkDev = ramDevCreate (0xc0000, 512, 400, 400, 0);
if (dosFsDevCreate ("DEV1:", pBlkDev, 20, NULL) == ERROR)
{
printErrno( );
}
In this case, only dosFsDevCreate( ) must be used, because the file system already
exists on the disk and does not require re-formatting. This procedure is useful if a
RAM disk is to be created at the same address used in a previous boot of VxWorks.
The contents of the RAM disk are then preserved.
NOTE: The ramDiskCbio library implements a RAM disk using the CBIO
interface; the ramDrv library implements a RAM disk using the BLK_DEV
interface.
For more information on RAM disk drivers, see the reference entry for ramDrv. For
more information on file systems, see 5. Local File Systems.
SCSI Drivers
SCSI is a standard peripheral interface that allows connection with a wide variety
of hard disks, optical disks, floppy disks, tape drives, and CD-ROM devices. SCSI
block drivers are compatible with the dosFs libraries, and offer several advantages
for target configurations. They provide:
146
4
I/O System
Description
INCLUDE_SCSI
INCLUDE_SCSI2
INCLUDE_SCSI_DMA
INCLUDE_SCSI_BOOT
SCSI_AUTO_CONFIG
INCLUDE_DOSFS
INCLUDE_TAPEFS
INCLUDE_CDROMFS
147
VxWorks 5.5
Programmers Guide
CAUTION: Including SCSI-2 in your VxWorks image can significantly increase the
image size.
Configuring the SCSI Bus ID
Each board in a SCSI-2 environment must define a unique SCSI bus ID for the SCSI
initiator. SCSI-1 drivers, which support only a single initiator at a time, assume an
initiator SCSI bus ID of 7. However, SCSI-2 supports multiple initiators, up to eight
initiators and targets at one time. Therefore, to ensure a unique ID, choose a value
in the range 0-7 to be passed as a parameter to the drivers initialization routine
(for example, ncr710CtrlInitScsi2( )) by the sysScsiInit( ) routine in sysScsi.c. For
more information, see the reference entry for the relevant driver initialization
routine. If there are multiple boards on one SCSI bus, and all of these boards use
the same BSP, then different versions of the BSP must be compiled for each board
by assigning unique SCSI bus IDs.
ROM Size Adjustment for SCSI Boot
The SCSI subsystem supports libraries and drivers for both SCSI-1 and SCSI-2. It
consists of the following six libraries which are independent of any SCSI controller:
scsiLib
routines that provide the mechanism for switching SCSI requests to either
the SCSI-1 library (scsi1Lib) or the SCSI-2 library (scsi2Lib), as configured
by the board support package (BSP).
scsi1Lib
SCSI-1 library routines and interface, used when only INCLUDE_SCSI is
used (see Configuring SCSI Drivers, p.147).
scsi2Lib
SCSI-2 library routines and all physical device creation and deletion
routines.
148
4
I/O System
scsiCommonLib
commands common to all types of SCSI devices.
scsiDirectLib
routines and commands for direct access devices (disks).
scsiSeqLib
routines and commands for sequential access block devices (tapes).
Controller-independent support for the SCSI-2 functionality is divided into
scsi2Lib, scsiCommonLib, scsiDirectLib, and scsiSeqLib. The interface to any of
these SCSI-2 libraries can be accessed directly. However, scsiSeqLib is designed to
be used in conjunction with tapeFs, while scsiDirectLib works with dosFs and
rawFs. Applications written for SCSI-1 can be used with SCSI-2; however, SCSI-1
device drivers cannot.
VxWorks targets using SCSI interface controllers require a controller-specific
device driver. These device drivers work in conjunction with the
controller-independent SCSI libraries, and they provide controller configuration
and initialization routines contained in controller-specific libraries. For example,
the Western Digital WD33C93 SCSI controller is supported by the device driver
libraries wd33c93Lib, wd33c93Lib1, and wd33c93Lib2. Routines tied to SCSI-1
(such as wd33c93CtrlCreate( )) and SCSI-2 (such as wd33c93CtrlCreateScsi2( ))
are segregated into separate libraries to simplify configuration. There are also
additional support routines for individual targets in sysLib.c.
Booting and Initialization
After VxWorks is built with SCSI support, the system startup code initializes the
SCSI interface by executing sysScsiInit( ) and usrScsiConfig( ) when
INCLUDE_SCSI is included in VxWorks. The call to sysScsiInit( ) initializes the
SCSI controller and sets up interrupt handling. The physical device configuration
is specified in usrScsiConfig( ), which is in installDir/target/src/config/usrScsi.c.
The routine contains an example of the calling sequence to declare a hypothetical
configuration, including:
149
VxWorks 5.5
Programmers Guide
SCSI messages
disconnects
minimum period and maximum REQ/ACK offset
tagged command queuing
wide data transfer
Device-specific options do not need to be set if the device shares this default
behavior. However, if you need to configure a device that diverges from these
default characteristics, use scsiTargetOptionsSet( ) to modify option values. These
options are fields in the SCSI_OPTIONS structure, shown below. SCSI_OPTIONS is
declared in scsi2Lib.h. You can choose to set some or all of these option values to
suit your particular SCSI device and application.
typedef struct
{
UINT
selTimeOut;
BOOL
messages;
BOOL
disconnect;
UINT8
maxOffset;
UINT8
minPeriod;
SCSI_TAG_TYPE tagType;
UINT
maxTags;
UINT8
xferWidth;
} SCSI_OPTIONS;
*/
*/
*/
*/
*/
*/
*/
*/
There are numerous types of SCSI devices, each supporting its own mix of SCSI-2
features. To set device-specific options, define a SCSI_OPTIONS structure and
assign the desired values to the structures fields. After setting the appropriate
fields, call scsiTargetOptionsSet( ) to effect your selections. Example 4-5 illustrates
one possible device configuration using SCSI_OPTIONS.
Call scsiTargetOptionsSet( ) after initializing the SCSI subsystem, but before
initializing the SCSI physical device. For more information about setting and
implementing options, see the reference entry for scsiTargetOptionsSet( ).
!
150
4
I/O System
If a device does not support SCSI messages, the boolean field messages can be set
to FALSE. Similarly, if a device does not support disconnect/reconnect, the
boolean field disconnect can be set to FALSE.
The SCSI subsystem automatically tries to negotiate synchronous data transfer
parameters. However, if a SCSI device does not support synchronous data transfer,
set the maxOffset field to 0. By default, the SCSI subsystem tries to negotiate the
maximum possible REQ/ACK offset and the minimum possible data transfer
period supported by the SCSI controller on the VxWorks target. This is done to
maximize the speed of transfers between two devices. However, speed depends
upon electrical characteristics, like cable length, cable quality, and device
termination; therefore, it may be necessary to reduce the values of maxOffset or
minPeriod for fast transfers.
The tagType field defines the type of tagged command queuing desired, using one
of the following macros:
SCSI_TAG_UNTAGGED
SCSI_TAG_SIMPLE
SCSI_TAG_ORDERED
SCSI_TAG_HEAD_OF_QUEUE
For more information about the types of tagged command queuing available, see
the ANSI X3T9-I/O Interface Specification Small Computer System Interface
(SCSI-2).
The maxTags field sets the maximum number of command tags available for a
particular SCSI device.
Wide data transfers with a SCSI target device are automatically negotiated upon
initialization by the SCSI subsystem. Wide data transfer parameters are always
negotiated before synchronous data transfer parameters, as specified by the SCSI
ANSI specification, because a wide negotiation resets any prior negotiation of
synchronous parameters. However, if a SCSI device does not support wide
parameters and there are problems initializing that device, you must set the
xferWidth field to 0. By default, the SCSI subsystem tries to negotiate the
maximum possible transfer width supported by the SCSI controller on the
VxWorks target in order to maximize the default transfer speed between the two
devices. For more information on the actual routine call, see the reference entry for
scsiTargetOptionsSet( ).
SCSI Configuration Examples
The following examples show some possible configurations for different SCSI
devices. Example 4-4 is a simple block device configuration setup. Example 4-5
151
VxWorks 5.5
Programmers Guide
If problems with your configuration occur, insert the following lines at the
beginning of usrScsiConfig( ) to obtain further information on SCSI bus activity.
#if FALSE
scsiDebug = TRUE;
scsiIntsDebug = TRUE;
#endif
152
4
I/O System
Do not declare the global variables scsiDebug and scsiIntsDebug locally. They can
be set or reset from the shell; see the Tornado Users Reference: Shell for details.
Example 4-5
Configuring a SCSI Disk Drive with Asynchronous Data Transfer and No Tagged Command
Queuing
In this example, a SCSI disk device is configured without support for synchronous
data transfer and tagged command queuing. The scsiTargetOptionsSet( ) routine
is used to turn off these features. The SCSI ID of this disk device is 2, and the LUN
is 0:
int
SCSI_OPTIONS
int
which;
option;
devBusId;
devBusId = 2;
which = SCSI_SET_OPT_XFER_PARAMS | SCSI_SET_OPT_TAG_PARAMS;
option.maxOffset = SCSI_SYNC_XFER_ASYNC_OFFSET;
/* => 0 defined in scsi2Lib.h */
option.minPeriod = SCSI_SYNC_XFER_MIN_PERIOD; /* defined in scsi2Lib.h */
option.tagType = SCSI_TAG_UNTAGGED;
/* defined in scsi2Lib.h */
option.maxTag = SCSI_MAX_TAGS;
if (scsiTargetOptionsSet (pSysScsiCtrl, devBusId, &option, which) == ERROR)
{
SCSI_DEBUG_MSG ("usrScsiConfig: could not set options\n", 0, 0, 0, 0,
0, 0);
return (ERROR);
}
/* configure SCSI disk drive at busId = devBusId, LUN = 0 */
if ((pSpd20 = scsiPhysDevCreate (pSysScsiCtrl, devBusId, 0, 0, NONE, 0, 0,
0)) == (SCSI_PHYS_DEV *) NULL)
{
SCSI_DEBUG_MSG ("usrScsiConfig: scsiPhysDevCreate failed.\n");
return (ERROR);
}
Example 4-6
153
VxWorks 5.5
Programmers Guide
Configuring a SCSI Disk for Synchronous Data Transfer with Non-Default Offset and Period
Values
In this example, a SCSI disk drive is configured with support for synchronous data
transfer. The offset and period values are user-defined and differ from the driver
default values. The chosen period is 25, defined in SCSI units of 4 ns. Thus, the
period is actually 4 * 25 = 100 ns. The synchronous offset is chosen to be 2. Note that
you may need to adjust the values depending on your hardware environment.
int
SCSI_OPTIONS
int
which;
option;
devBusId;
devBusId = 2;
which = SCSI_SET_IPT_XFER_PARAMS;
option.maxOffset = 2;
option.minPeriod = 25;
if (scsiTargetOptionsSet (pSysScsiCtrl, devBusId &option, which) ==
ERROR)
154
4
I/O System
{
SCSI_DEBUG_MSG ("usrScsiConfig: could not set options\n",
0, 0, 0, 0, 0, 0)
return (ERROR);
}
/* configure SCSI disk drive at busId = devBusId, LUN = 0 */
if ((pSpd20 = scsiPhysDevCreate (pSysScsiCtrl, devBusId, 0, 0, NONE,
0, 0, 0)) == (SCSI_PHYS_DEV *) NULL)
{
SCSI_DEBUG_MSG ("usrScsiConfig: scsiPhysDevCreate failed.\n")
return (ERROR);
}
Example 4-8
To change the bus ID of the SCSI controller, modify sysScsiInit( ) in sysScsi.c. Set
the SCSI bus ID to a value between 0 and 7 in the call to xxxCtrlInitScsi2( ), where
xxx is the controller name. The default bus ID for the SCSI controller is 7.
Troubleshooting
Applications written for SCSI-1 may not execute for SCSI-2 because data
structures in scsi2Lib.h, such as SCSI_TRANSACTION and SCSI_PHYS_DEV,
have changed. This applies only if the application used these structures
directly.
If this is the case, you can choose to configure only the SCSI-1 level of support,
or you can modify your application according to the data structures in
scsi2Lib.h. In order to set new fields in the modified structure, some
applications may simply need to be recompiled, and some applications will
have to be modified and then recompiled.
If your SCSI bus hangs, it could be for a variety of reasons. Some of the more
common are:
Your cable has a defect. This is the most common cause of failure.
155
VxWorks 5.5
Programmers Guide
The driver is trying to negotiate wide data transfers on a device that does
not support them. In rejecting wide transfers, the device-specific driver
cannot handle this phase mismatch. Use scsiTargetOptionsSet( ) to set the
appropriate value for the xferWidth field for that particular SCSI device.
4.7.8 Sockets
In VxWorks, the underlying basis of network communications is sockets. A socket
is an endpoint for communication between tasks; data is sent from one socket to
another. Sockets are not created or opened using the standard I/O functions.
Instead, they are created by calling socket( ), and connected and accessed using
other routines in sockLib. However, after a stream socket (using TCP) is created
and connected, it can be accessed as a standard I/O device, using read( ), write( ),
ioctl( ), and close( ). The value returned by socket( ) as the socket handle is in fact
an I/O system file descriptor (fd).
VxWorks socket routines are source-compatible with the BSD 4.4 UNIX socket
functions and the Windows Sockets (Winsock 1.1) networking standard. Use of
these routines is discussed in VxWorks Network Programmers Guide: Networking
APIs.
removed dynamically.
File Descriptors. In UNIX and Windows, fds are unique to each process. In
VxWorks, fds are global entities, accessible by any task, except for standard
input, standard output, and standard error (0, 1, and 2), which can be task
specific.
156
4
I/O System
I/O Control. The specific parameters passed to ioctl( ) functions can differ
Driver Routines. In UNIX, device drivers execute in system mode and cannot
157
VxWorks 5.5
Programmers Guide
There are two fundamental types of device: block and character (or non-block; see
Figure 4-8). Block devices are used for storing file systems. They are random access
devices where data is transferred in blocks. Examples of block devices include
hard and floppy disks. Character devices are any device that does not fall in the
block category. Examples of character devices include serial and graphical input
devices, for example, terminals and graphics tablets.
As discussed in earlier sections, the three main elements of the VxWorks I/O
system are drivers, devices, and files. The following sections describe these
elements in detail. The discussion focuses on character drivers; however, much of
it is applicable for block devices. Because block drivers must interact with
VxWorks file systems, they use a slightly different organization; see 4.9.4 Block
Devices, p.176.
NOTE: This discussion is designed to clarify the structure of VxWorks I/O facilities
and to highlight some considerations relevant to writing I/O drivers for VxWorks.
It is not a complete text on writing a device driver. For detailed information on this
subject, see the VxWorks BSP Developers Guide.
Example 4-9 shows the abbreviated code for a hypothetical driver that is used as
an example throughout the following discussions. This example driver is typical
of drivers for character-oriented devices.
In VxWorks, each driver has a short, unique abbreviation, such as net or tty, which
is used as a prefix for each of its routines. The abbreviation for the example driver
is xx.
Example 4-9
Hypothetical Driver
/*
* xxDrv - driver initialization routine
* xxDrv() inits the driver. It installs the driver via iosDrvInstall.
* It may allocate data structures, connect ISRs, and initialize hardware
*/
STATUS xxDrv ()
{
xxDrvNum = iosDrvInstall (xxCreat, 0, xxOpen, 0, xxRead, xxWrite, xxIoctl)
;
(void) intConnect (intvec, xxInterrupt, ...);
...
}
/************************************************************************
* xxDevCreate - device creation routine
*
* Called to add a device called <name> to be svced by this driver. Other
* driver-dependent arguments may include buffer sizes, device addresses.
158
4
I/O System
* The routine adds the device to the I/O system by calling iosDevAdd.
* It may also allocate and initialize data structures for the device,
* initialize semaphores, initialize device hardware, and so on.
*/
STATUS xxDevCreate (name, ...)
char * name;
...
{
status = iosDevAdd (xxDev, name, xxDrvNum);
...
}
/*
*
* The following routines implement the basic I/O functions.
* The xxOpen() return value is meaningful only to this driver,
* and is passed back as an argument to the other I/O routines.
*/
int xxOpen (xxDev, remainder, mode)
XXDEV * xxDev;
char * remainder;
int mode;
{
/* serial devices should have no file name part */
if (remainder[0] != 0)
return (ERROR);
else
return ((int) xxDev);
}
int xxRead (xxDev, buffer, nBytes)
XXDEV * xxDev;
char * buffer;
int nBytes;
...
int xxWrite (xxDev, buffer, nBytes)
...
int xxIoctl (xxDev, requestCode, arg)
...
/*
*
*
*
*
*
*
*/
159
VxWorks 5.5
Programmers Guide
4.9.1 Drivers
A driver for a non-block device implements the seven basic I/O functions
creat( ), delete( ), open( ), close( ), read( ), write( ), and ioctl( )for a particular
kind of device. In general, this type of driver has routines that implement each of
these functions, although some of the routines can be omitted if the functions are
not operative with that device.
Drivers can optionally allow tasks to wait for activity on multiple file descriptors.
This is implemented using the drivers ioctl( ) routine; see Implementing select( ),
p.168.
A driver for a block device interfaces with a file system, rather than directly with
the I/O system. The file system in turn implements most I/O functions. The driver
need only supply routines to read and write blocks, reset the device, perform I/O
control, and check device status. Drivers for block devices have a number of
special requirements that are discussed in 4.9.4 Block Devices, p.176.
When the user invokes one of the basic I/O functions, the I/O system routes the
request to the appropriate routine of a specific driver, as detailed in the following
sections. The drivers routine runs in the calling tasks context, as though it were
called directly from the application. Thus, the driver is free to use any facilities
normally available to tasks, including I/O to other devices. This means that most
drivers have to use some mechanism to provide mutual exclusion to critical
regions of code. The usual mechanism is the semaphore facility provided in
semLib.
In addition to the routines that implement the seven basic I/O functions, drivers
also have three other routines:
An initialization routine that installs the driver in the I/O system, connects to
any interrupts used by the devices serviced by the driver, and performs any
necessary hardware initialization. This routine is typically named xxDrv( ).
A routine to add devices that are to be serviced by the driver to the I/O system.
This routine is typically named xxDevCreate( ).
160
4
I/O System
The function of the I/O system is to route user I/O requests to the appropriate
routine of the appropriate driver. The I/O system does this by maintaining a table
that contains the address of each routine for each driver. Drivers are installed
dynamically by calling the I/O system internal routine iosDrvInstall( ). The
arguments to this routine are the addresses of the seven I/O routines for the new
driver. The iosDrvInstall( ) routine enters these addresses in a free slot in the
driver table and returns the index of this slot. This index is known as the driver
number and is used subsequently to associate particular devices with the driver.
Null (0) addresses can be specified for some of the seven routines. This indicates
that the driver does not process those functions. For non-file-system drivers,
close( ) and delete( ) often do nothing as far as the driver is concerned.
VxWorks file systems (such as dosFsLib) contain their own entries in the driver
table, which are created when the file system library is initialized.
Figure 4-2
DRIVER CALL:
drvnum = iosDrvInstall (xxCreat, 0, xxOpen, 0, xxRead, xxWrite, xxIoctl);
[1] Drivers install routine specifies driver
routines for seven I/O functions.
[2] I/O system locates next
available slot in driver table.
DRIVER TABLE:
create
0
1
2 xxCreat
3
4
open
close
xxOpen
read
write
ioctl
xxReadxxWrite xxIoctl
delete
161
VxWorks 5.5
Programmers Guide
Figure 4-2 shows the actions taken by the example driver and by the I/O system
when the initialization routine xxDrv( ) runs.
The driver calls iosDrvInstall( ), specifying the addresses of the drivers routines
for the seven basic I/O functions. Then, the I/O system:
1.
Locates the next available slot in the driver table, in this case slot 2.
2.
3.
Returns the slot number as the driver number of the newly installed driver.
4.9.2 Devices
Some drivers are capable of servicing many instances of a particular kind of device.
For example, a single driver for a serial communications device can often handle
many separate channels that differ only in a few parameters, such as device
address.
In the VxWorks I/O system, devices are defined by a data structure called a device
header (DEV_HDR). This data structure contains the device name string and the
driver number for the driver that services this device. The device headers for all
the devices in the system are kept in a memory-resident linked list called the device
list. The device header is the initial part of a larger structure determined by the
individual drivers. This larger structure, called a device descriptor, contains
additional device-specific data such as device addresses, buffers, and semaphores.
Non-block devices are added to the I/O system dynamically by calling the internal
I/O routine iosDevAdd( ). The arguments to iosDevAdd( ) are the address of the
device descriptor for the new device, the devices name, and the driver number of
the driver that services the device. The device descriptor specified by the driver
can contain any necessary device-dependent information, as long as it begins with
a device header. The driver does not need to fill in the device header, only the
device-dependent information. The iosDevAdd( ) routine enters the specified
device name and the driver number in the device header and adds it to the system
device list.
162
4
I/O System
To add a block device to the I/O system, call the device initialization routine for
the file system required on that device (dosFsDevCreate( ) or rawFsDevInit( )).
The device initialization routine then calls iosDevAdd( ) automatically.
The routine iosDevFind( ) can be used to locate the device structure (by obtaining
a pointer to the DEV_HDR, which is the first member of that structure) and to verify
that a device name exists in the table. Following is an example using
iosDevFind( ):
char * pTail;
/* pointer to tail of devName */
char devName[6] = DEV1:;
/* name of device */
DOS_VOLUME_DESC * pDosVolDesc;
/* first member is DEV_HDR */
...
pDosVolDesc = iosDevFind(devName, (char**)&pTail);
if (NULL == pDosVolDesc)
{
/* ERROR: device name does not exist and no default device */
}
else
{
/*
* pDosVolDesc is a valid DEV_HDR pointer
* and pTail points to beginning of devName.
* Check devName against pTail to determine if it is
* the default name or the specified devName.
*/
}
In Figure 4-3, the example drivers device creation routine xxDevCreate( ) adds
devices to the I/O system by calling iosDevAdd( ).
163
VxWorks 5.5
Programmers Guide
Figure 4-3
DRIVER CALLS:
DEVICE LIST:
"/dk0/"
1
DRIVER TABLE:
"/xx0"
2
"/xx1"
2
devicedependent
data
devicedependent
data
create delete
open
close
read
write
ioctl
0
1
2
3
4
The Fd Table
Files are opened with open( ) (or creat( )). The I/O system searches the device list
for a device name that matches the filename (or an initial substring) specified by
the caller. If a match is found, the I/O system uses the driver number contained in
the corresponding device header to locate and call the drivers open routine in the
driver table.
The I/O system must establish an association between the file descriptor used by
the caller in subsequent I/O calls, and the driver that services it. Additionally, the
driver must associate some data structure per descriptor. In the case of non-block
devices, this is usually the device descriptor that was located by the I/O system.
164
4
I/O System
The I/O system maintains these associations in a table called the fd table. This table
contains the driver number and an additional driver-determined 4-byte value. The
driver value is the internal descriptor returned by the drivers open routine, and
can be any nonnegative value the driver requires to identify the file. In subsequent
calls to the drivers other I/O functions (read( ), write( ), ioctl( ), and close( )), this
value is supplied to the driver in place of the fd in the application-level I/O call.
In Figure 4-4 and Figure 4-5, a user calls open( ) to open the file /xx0. The I/O
system takes the following series of actions:
1.
It searches the device list for a device name that matches the specified filename
It reserves a slot in the fd table and creates a new file descriptor object, which
is used if the open is successful.
3.
It then looks up the address of the drivers open routine, xxOpen( ), and calls
that routine. Note that the arguments to xxOpen( ) are transformed by the I/O
system from the users original arguments to open( ). The first argument to
xxOpen( ) is a pointer to the device descriptor the I/O system located in the
full filename search. The next parameter is the remainder of the filename
specified by the user, after removing the initial substring that matched the
device name. In this case, because the device name matched the entire
filename, the remainder passed to the driver is a null string. The driver is free
to interpret this remainder in any way it wants. In the case of block devices,
this remainder is the name of a file on the device. In the case of non-block
devices like this one, it is usually an error for the remainder to be anything but
the null string. The third parameter is the file access flag, in this case
O_RDONLY; that is, the file is opened for reading only. The last parameter is the
mode, as passed to the original open( ) routine.
4.
5.
The I/O system then enters the driver number and the value returned by
xxOpen( ) in the new file descriptor object. .
165
VxWorks 5.5
Programmers Guide
Figure 4-4
USER CALL:
DRIVER CALL:
pDevHdr value
FD TABLE:
0
1
2
3
4
DEVICE LIST:
"/dk0/"
1
"/xx0"
2
"/xx1"
2
devicedependent
data
DRIVER TABLE:
create delete
0
1
2
3
4
open
close
read
write
ioctl
xxOpen
Again, the value entered in the fd object has meaning only for the driver, and
is arbitrary as far as the I/O system is concerned.
6.
166
Finally, it returns to the user the index of the slot in the fd table, in this case 3.
4
I/O System
Figure 4-5
USER CALL:
DRIVER CALL:
drvnum value
FD TABLE:
0
1
2
3
4
xxdev
DEVICE LIST:
"/dk0/"
1
"/xx0"
2
"/xx1"
2
devicedependent
data
create delete
DRIVER TABLE:
open
close
read
write
0
1
2
3
4
167
ioctl
VxWorks 5.5
Programmers Guide
In Figure 4-6, the user calls read( ) to obtain input data from the file. The specified
fd is the index into the fd table for this file. The I/O system uses the driver number
contained in the table to locate the drivers read routine, xxRead( ). The I/O system
calls xxRead( ), passing it the identifying value in the fd table that was returned by
the drivers open routine, xxOpen( ). Again, in this case the value is the pointer to
the device descriptor. The drivers read routine then does whatever is necessary to
read data from the device. The process for user calls to write( ) and ioctl( ) follow
the same procedure.
The user terminates the use of a file by calling close( ). As in the case of read( ), the
I/O system uses the driver number contained in the fd table to locate the drivers
close routine. In the example driver, no close routine is specified; thus no driver
routines are called. Instead, the I/O system marks the slot in the fd table as being
available. Any subsequent references to that fd cause an error. However,
subsequent calls to open( ) can reuse that slot.
Implementing select( )
Supporting select( ) in your driver allows tasks to wait for input from multiple
devices or to specify a maximum time to wait for the device to become ready for
I/O. Writing a driver that supports select( ) is simple, because most of the
functionality is provided in selectLib. You might want your driver to support
select( ) if any of the following is appropriate for the device:
The tasks want to specify a timeout to wait for I/O from the device. For
example, a task might want to time out on a UDP socket if the packet never
arrives.
The driver supports multiple devices, and the tasks want to wait
simultaneously for any number of them. For example, multiple pipes might be
used for different data priorities.
The tasks want to wait for I/O from the device while also waiting for I/O from
another device. For example, a server task might use both pipes and sockets.
To implement select( ), the driver must keep a list of tasks waiting for device
activity. When the device becomes ready, the driver unblocks all the tasks waiting
on the device.
168
4
I/O System
Figure 4-6
USER CALL:
DRIVER CALL:
drvnum value
FD TABLE:
0
1
2
3
4
xxdev
DEVICE LIST:
"/dk0/"
1
"/xx0"
2
"/xx1"
2
devicedependent
data
DRIVER TABLE:
close
read
write
ioctl
xxRead
169
VxWorks 5.5
Programmers Guide
xxDevCreate( ) routine. When a task calls select( ), selectLib calls the drivers
ioctl( ) routine with the function FIOSELECT or FIOUNSELECT. If ioctl( ) is called
with FIOSELECT, the driver must do the following:
1.
2.
Use the routine selWakeupType( ) to check whether the task is waiting for
data to read from the device (SELREAD) or if the device is ready to be written
(SELWRITE).
3.
170
/*
/*
/*
/*
device header */
data is available to read */
device is ready to write */
list of tasks pended in select */
4
I/O System
4
/* name of device to create */
switch (request)
{
... additional driver code ...
case FIOSELECT:
/* add node to wakeup list */
selNodeAdd (&pMyDrvDev->selWakeupList, (SEL_WAKEUP_NODE *) arg);
if (selWakeupType ((SEL_WAKEUP_NODE *) arg) == SELREAD
&& pMyDrvDev->myDrvDataAvailable)
{
/* data available, make sure task does not pend */
selWakeup ((SEL_WAKEUP_NODE *) arg);
}
if (selWakeupType ((SEL_WAKEUP_NODE *) arg) == SELWRITE
171
VxWorks 5.5
Programmers Guide
&& pMyDrvDev->myDrvRdyForWriting)
{
/* device ready for writing, make sure task does not pend */
selWakeup ((SEL_WAKEUP_NODE *) arg);
}
break;
case FIOUNSELECT:
/* delete node from wakeup list */
selNodeDelete (&pMyDrvDev->selWakeupList, (SEL_WAKEUP_NODE *) arg);
break;
... additional driver code ...
}
}
/* code that actually uses the select() function to read or write */
void myDrvIsr
(
MY_DEV * pMyDrvDev;
)
{
... additional driver code ...
/* if there is data available to read, wake up all pending tasks */
if (pMyDrvDev->myDrvDataAvailable)
selWakeupAll (&pMyDrvDev->selWakeupList, SELREAD);
/* if the device is ready to write, wake up all pending tasks */
if (pMyDrvDev->myDrvRdyForWriting)
selWakeupAll (&pMyDrvDev->selWakeupList, SELWRITE);
}
Cache Coherency
Drivers written for boards with caches must guarantee cache coherency. Cache
coherency means data in the cache must be in sync, or coherent, with data in RAM.
The data cache and RAM can get out of sync any time there is asynchronous access
to RAM (for example, DMA device access or VMEbus access). Data caches are used
to increase performance by reducing the number of memory accesses. Figure 4-7
shows the relationships between the CPU, data cache, RAM, and a DMA device.
Data caches can operate in one of two modes: writethrough and copyback.
Write-through mode writes data to both the cache and RAM; this guarantees cache
coherency on output but not input. Copyback mode writes the data only to the
cache; this makes cache coherency an issue for both input and output of data.
172
4
I/O System
Figure 4-7
Cache Coherency
CPU
4
Data Cache
RAM
DMA
Device
If a CPU writes data to RAM that is destined for a DMA device, the data can first
be written to the data cache. When the DMA device transfers the data from RAM,
there is no guarantee that the data in RAM was updated with the data in the cache.
Thus, the data output to the device may not be the most recentthe new data may
still be sitting in the cache. This data incoherence can be solved by making sure the
data cache is flushed to RAM before the data is transferred to the DMA device.
If a CPU reads data from RAM that originated from a DMA device, the data read
can be from the cache buffer (if the cache buffer for this data is not marked invalid)
and not the data just transferred from the device to RAM. The solution to this data
incoherence is to make sure that the cache buffer is marked invalid so that the data
is read from RAM and not from the cache.
Drivers can solve the cache coherency problem either by allocating cache-safe
buffers (buffers that are marked non-cacheable) or flushing and invalidating cache
entries any time the data is written to or read from the device. Allocating
cache-safe buffers is useful for static buffers; however, this typically requires MMU
support. Non-cacheable buffers that are allocated and freed frequently (dynamic
buffers) can result in large amounts of memory being marked non-cacheable. An
alternative to using non-cacheable buffers is to flush and invalidate cache entries
manually; this allows dynamic buffers to be kept coherent.
The routines cacheFlush( ) and cacheInvalidate( ) are used to manually flush and
invalidate cache buffers. Before a device reads the data, flush the data from the
cache to RAM using cacheFlush( ) to ensure the device reads current data. After
the device has written the data into RAM, invalidate the cache entry with
cacheInvalidate( ). This guarantees that when the data is read by the CPU, the
cache is updated with the new data in RAM.
173
VxWorks 5.5
Programmers Guide
Example 4-11
4
I/O System
Address-Translation Driver
4
/* The following code is an example of a driver that performs address
* translations. It attempts to allocate a cache-safe buffer, fill it, and
* then write it out to the device. It uses CACHE_DMA_FLUSH to make sure
* the data is current. The driver then reads in new data and uses
* CACHE_DMA_INVALIDATE to guarantee cache coherency.
*/
#include "vxWorks.h"
#include "cacheLib.h"
#include "myExample.h"
STATUS myDmaExample (void)
{
void * pMyBuf;
void * pPhysAddr;
/* allocate cache safe buffers if possible */
if ((pMyBuf = cacheDmaMalloc (MY_BUF_SIZE)) == NULL)
return (ERROR);
fill buffer with useful information
/* flush cache entry before data is written to device */
CACHE_DMA_FLUSH (pMyBuf, MY_BUF_SIZE);
/* convert virtual address to physical */
pPhysAddr = CACHE_DMA_VIRT_TO_PHYS (pMyBuf);
/* program device to read data from RAM */
myBufToDev (pPhysAddr);
wait for DMA to complete
ready to read new data
/* program device to write data to RAM */
myDevToBuf (pPhysAddr);
wait for transfer to complete
/* convert physical to virtual address */
pMyBuf = CACHE_DMA_PHYS_TO_VIRT (pPhysAddr);
/* invalidate buffer */
CACHE_DMA_INVALIDATE (pMyBuf, MY_BUF_SIZE);
use data
/* when done free memory */
if (cacheDmaFree (pMyBuf) == ERROR)
return (ERROR);
return (OK);
}
175
VxWorks 5.5
Programmers Guide
In VxWorks, block devices have a slightly different interface than other I/O
devices. Rather than interacting directly with the I/O system, block device drivers
interact with a file system. The file system, in turn, interacts with the I/O system.
Direct access block devices have been supported since SCSI-1 and are used
compatibly with dosFs and rawFs. In addition, VxWorks supports SCSI-2
sequential devices, which are organized so individual blocks of data are read and
written sequentially. When data blocks are written, they are added sequentially at
the end of the written medium; that is, data blocks cannot be replaced in the
middle of the medium. However, data blocks can be accessed individually for
reading throughout the medium. This process of accessing data on a sequential
medium differs from that of other block devices.
Figure 4-8 shows a layered model of I/O for both block and non-block (character)
devices. This layered arrangement allows the same block device driver to be used
with different file systems, and reduces the number of I/O functions that must be
supported in the driver.
A device driver for a block device must provide a means for creating a logical block
device structure, a BLK_DEV for direct access block devices or a SEQ_DEV for
sequential block devices. The BLK_DEV/SEQ_DEV structure describes the device
in a generic fashion, specifying only those common characteristics that must be
known to a file system being used with the device. Fields within the structures
specify various physical configuration variables for the devicefor example, block
size, or total number of blocks. Other fields in the structures specify routines
within the device driver that are to be used for manipulating the device (reading
blocks, writing blocks, doing I/O control functions, resetting the device, and
checking device status). The BLK_DEV/SEQ_DEV structures also contain fields
used by the driver to indicate certain conditions (for example, a disk change) to the
file system.
When the driver creates the block device, the device has no name or file system
associated with it. These are assigned during the device initialization routine for
the chosen file system (for example, dosFsDevInit( ) or tapeFsDevInit( )).
The low-level device driver for a block device is not installed in the I/O system
driver table, unlike non-block device drivers. Instead, each file system in the
VxWorks system is installed in the driver table as a driver. Each file system has
only one entry in the table, even though several different low-level device drivers
can have devices served by that file system.
176
4
I/O System
Figure 4-8
Application
4
I/O System
driver table
File System
dosFs, rawFs,
or tapeFs
Non-Block
Device Driver
Block
Device Driver
Device(s)
Device(s)
After a device is initialized for use with a particular file system, all I/O operations
for the device are routed through that file system. To perform specific device
operations, the file system in turn calls the routines in the specified BLK_DEV or
SEQ_DEV structure.
A driver for a block device must provide the interface between the device and
VxWorks. There is a specific set of functions required by VxWorks; individual
devices vary based on what additional functions must be provided. The user
manual for the device being used, as well as any other drivers for the device, is
invaluable in creating the VxWorks driver.
177
VxWorks 5.5
Programmers Guide
The following sections describe the components necessary to build low-level block
device drivers that adhere to the standard interface for VxWorks file systems.
The driver normally requires a general initialization routine. This routine performs
all operations that are done one time only, as opposed to operations that must be
performed for each device served by the driver. As a general guideline, the
operations in the initialization routine affect the whole device controller, while
later operations affect only specific devices.
Common operations in block device driver initialization routines include:
initializing hardware
allocating and initializing data structures
creating semaphores
initializing interrupt vectors
enabling interrupts
The operations performed in the initialization routine are entirely specific to the
device (controller) being used; VxWorks has no requirements for a driver
initialization routine.
Unlike non-block device drivers, the driver initialization routine does not call
iosDrvInstall( ) to install the driver in the I/O system driver table. Instead, the file
system installs itself as a driver and routes calls to the actual driver using the
routine addresses placed in the block device structure, BLK_DEV or SEQ_DEV (see
Device Creation Routine, p.178).
The driver must provide a routine to create (define) a logical disk or sequential
device. A logical disk device may be only a portion of a larger physical device. If
this is the case, the device driver must keep track of any block offset values or other
means of identifying the physical area corresponding to the logical device.
VxWorks file systems always use block numbers beginning with zero for the start
of a device. A sequential access device can be either of variable block size or fixed
block size. Most applications use devices of fixed block size.
The device creation routine generally allocates a device descriptor structure that
the driver uses to manage the device. The first item in this device descriptor must
be a VxWorks block device structure (BLK_DEV or SEQ_DEV). It must appear first
178
4
I/O System
because its address is passed by the file system during calls to the driver; having
the BLK_DEV or SEQ_DEV as the first item permits also using this address to
identify the device descriptor.
The device creation routine must initialize the fields within the BLK_DEV or
SEQ_DEV structure. The BLK_DEV fields and their initialization values are shown
in Table 4-15.
Table 4-15
Value
bd_blkRd
Address of the driver routine that reads blocks from the device.
bd_blkWrt
bd_ioctl
bd_reset
Address of the driver routine that resets the device (NULL if none).
bd_statusChk
bd_removable
bd_nBlocks
bd_bytesPerBlk
bd_blksPerTrack
bd_nHeads
bd_retry
bd_mode
bd_readyChanged
The SEQ_DEV fields and their initialization values are shown in Table 4-16.
The device creation routine returns the address of the BLK_DEV or SEQ_DEV
structure. This address is then passed during the file system device initialization
call to identify the device.
179
VxWorks 5.5
Programmers Guide
Table 4-16
Value
sd_seqRd
Address of the driver routine that reads blocks from the device.
sd_seqWrt
sd_ioctl
sd_seqWrtFileMarks
Address of the driver routine that writes file marks to the device.
sd_rewind
sd_reserve
sd_release
sd_readBlkLim
Address of the driver routine that reads the data block limits from
the sequential device.
sd_load
sd_space
sd_erase
sd_reset
sd_statusChk
sd_blkSize
sd_mode
sd_readyChanged
Value for specifying whether or not the device ready status has
changed. TRUE if the status has changed; initialize to TRUE to
cause the sequential device to be mounted.
180
4
I/O System
Unlike non-block device drivers, the device creation routine for a block device
does not call iosDevAdd( ) to install the device in the I/O system device table.
Instead, this is done by the file systems device initialization routine.
The driver must supply a routine to read one or more blocks from the device. For
a direct access device, the read-blocks routine must have the following arguments
and result:
STATUS xxBlkRd
(
DEVICE * pDev,
int
startBlk,
int
numBlks,
char *
pBuf
)
/*
/*
/*
/*
NOTE: In this and following examples, the routine names begin with xx. These
names are for illustration only, and do not have to be used by your device driver.
VxWorks references the routines by address only; the name can be anything.
pDev
startBlk
the starting block number to be read from the device. The file system
always uses block numbers beginning with zero for the start of the
device. Any offset value used for this logical device must be added in
by the driver.
numBlks
pBuf
181
VxWorks 5.5
Programmers Guide
The driver must supply a routine to read a specified number of bytes from the
device. The bytes being read are always assumed to be read from the current
location of the read/write head on the media. The read routine must have the
following arguments and result:
STATUS xxSeqRd
(
DEVICE * pDev,
int
numBytes,
char *
buffer,
BOOL
fixed
)
/*
/*
/*
/*
pDev
numBytes
buffer
fixed
specifies whether the read routine reads fixed-size blocks from the
sequential device or variable-sized blocks, as specified by the file
system. If fixed is TRUE, fixed-size blocks are used.
The driver must supply a routine to write one or more blocks to the device. The
definition of this routine closely parallels that of the read routine. For direct-access
devices, the write routine is as follows:
STATUS xxBlkWrt
(
DEVICE * pDev,
int
startBlk,
int
numBlks,
char *
pBuf
)
182
/*
/*
/*
/*
4
I/O System
pDev
startBlk
numBlks
pBuf
The driver must supply a routine to write a specified number of bytes to the device.
The bytes being written are always assumed to be written to the current location
of the read/write head on the media. For sequential devices, the write routine is as
follows:
STATUS xxWrtTape
(
DEVICE * pDev,
int
numBytes,
char *
buffer,
BOOL
fixed
)
/*
/*
/*
/*
pDev
numBytes
buffer
fixed
specifies whether the write routine reads fixed-size blocks from the
sequential device or variable-sized blocks, as specified by the file
system. If fixed is TRUE, fixed-size blocks are used.
The driver must provide a routine that can handle I/O control requests. In
VxWorks, most I/O operations beyond basic file handling are implemented
through ioctl( ) functions. The majority of these are handled directly by the file
183
VxWorks 5.5
Programmers Guide
system. However, if the file system does not recognize a request, that request is
passed to the drivers I/O control routine.
Define the drivers I/O control routine as follows:
STATUS xxIoctl
(
DEVICE * pDev,
int
funcCode,
int
arg
)
pDev
funcCode
arg
The drivers I/O control routine typically takes the form of a multi-way switch
statement, based on the function code. The drivers I/O control routine must
supply a default case for function code requests it does not recognize. For such
requests, the I/O control routine sets errno to S_ioLib_UNKNOWN_REQUEST and
returns ERROR.
The drivers I/O control routine returns OK if it handled the request successfully;
otherwise, it returns ERROR.
Device-Reset Routine
The driver usually supplies a routine to reset a specific device, but it is not
required. This routine is called when a VxWorks file system first mounts a disk or
tape, and again during retry operations when a read or write fails.
Declare the drivers device-reset routine as follows:
STATUS xxReset
(
DEVICE * pDev
)
184
4
I/O System
When called, this routine resets the device and controller. Do not reset other
devices, if it can be avoided. The routine returns OK if the driver succeeded in
resetting the device; otherwise, it returns ERROR.
If no reset operation is required for the device, this routine can be omitted. In this
case, the device-creation routine sets the xx_reset field in the BLK_DEV or
SEQ_DEV structure to NULL.
NOTE: In this and following examples, the names of fields in the BLK_DEV and
SEQ_DEV structures are parallel except for the initial letters bd_ or sd_. In these
cases, the initial letters are represented by xx_, as in the xx_reset field to represent
both the bd_reset field and the sd_reset field.
Status-Check Routine
If the driver provides a routine to check device status or perform other preliminary
operations, the file system calls this routine at the beginning of each open( ) or
creat( ) on the device.
Define the status-check routine as follows:
STATUS xxStatusChk
(
DEVICE * pDev
)
The routine returns OK if the open or create operation can continue. If it detects a
problem with the device, it sets errno to some value indicating the problem, and
returns ERROR. If ERROR is returned, the file system does not continue the
operation.
A primary use of the status-check routine is to check for a disk change on devices
that do not detect the change until after a new disk is inserted. If the routine
determines that a new disk is present, it sets the bd_readyChanged field in the
BLK_DEV structure to TRUE and returns OK so that the open or create operation
can continue. The new disk is then mounted automatically by the file system. (See
Change in Ready Status, p.186.)
Similarly, the status check routine can be used to check for a tape change. This
routine determines whether a new tape has been inserted. If a new tape is present,
the routine sets the sd_readyChanged field in the SEQ_DEV structure to TRUE and
returns OK so that the open or create operation can continue. The device driver
should not be able to unload a tape, nor should a tape be physically ejected, while
a file descriptor is open on the tape device.
185
VxWorks 5.5
Programmers Guide
Write-Protected Media
The device driver may detect that the disk or tape in place is write-protected. If this
is the case, the driver sets the xx_mode field in the BLK_DEV or SEQ_DEV structure
to O_RDONLY. This can be done at any time (even after the device is initialized for
use with the file system). The file system respects the xx_mode field setting and
does not allow writes to the device until the xx_mode field is changed to O_RDWR
or O_WRONLY.
The driver informs the file system whenever a change in the devices ready status
is recognized. This can be the changing of a floppy disk, changing of the tape
medium, or any other situation that makes it advisable for the file system to
remount the disk.
To announce a change in ready status, the driver sets the xx_readyChanged field
in the BLK_DEV or SEQ_DEV structure to TRUE. This is recognized by the file
system, which remounts the disk during the next I/O initiated on the disk. The file
system then sets the xx_readyChanged field to FALSE. The xx_readyChanged
field is never cleared by the device driver.
Setting xx_readyChanged to TRUE has the same effect as calling the file systems
ready-change routine (for example, calling ioctl( ) with the FIODISKCHANGE
function code).
An optional status-check routine (see Status-Check Routine, p.185) can provide a
convenient mechanism for asserting a ready-change, particularly for devices that
cannot detect a disk change until after the new disk is inserted. If the status-check
routine detects that a new disk is present, it sets xx_readyChanged to TRUE. This
routine is called by the file system at the beginning of each open or create
operation.
The sequential driver must provide a routine that can write file marks onto the tape
device. The write file marks routine must have the following arguments:
186
4
I/O System
STATUS xxWrtFileMarks
(
DEVICE * pDev,
int
numMarks,
BOOL
shortMark
)
pDev
the type of file mark (short or long). If shortMark is TRUE, short marks
are written.
The write file marks routine returns OK if the file marks are written correctly on
the tape device; otherwise, it returns ERROR.
The sequential driver must provide a rewind routine in order to rewind tapes in
the tape device. The rewind routine is defined as follows:
STATUS xxRewind
(
DEVICE * pDev
)
When called, this routine rewinds the tape in the tape device. The routine returns
OK if completion is successful; otherwise, it returns ERROR.
The sequential driver can provide a reserve routine that reserves the physical tape
device for exclusive access by the host that is executing the reserve routine. The
tape device remains reserved until it is released by that host, using a release
routine, or by some external stimulus.
The reserve routine is defined as follows:
STATUS xxReserve
(
DEVICE * pDev
)
If a tape device is reserved successfully, the reserve routine returns OK. However,
if the tape device cannot be reserved or an error occurs, it returns ERROR.
187
VxWorks 5.5
Programmers Guide
This routine releases the exclusive access that a host has on a tape device. The tape
device is then free to be reserved again by the same host or some other host. This
routine is the opposite of the reserve routine and must be provided by the driver if
the reserve routine is provided.
The release routine is defined as follows:
STATUS xxReset
(
DEVICE * pDev
)
If the tape device is released successfully, this routine returns OK. However, if the
tape device cannot be released or an error occurs, this routine returns ERROR.
The read-block-limits routine can poll a tape device for its physical block limits.
These block limits are then passed back to the file system so the file system can
decide the range of block sizes to be provided to a user.
The read-block-limits routine is defined as follows:
STATUS xxReadBlkLim
(
DEVICE * pDev,
int
*maxBlkLimit,
int
*minBlkLimit
)
pDev
maxBlkLimit
returns the maximum block size that the tape device can handle to the
calling tape file system.
minBlkLimit
returns the minimum block size that the tape device can handle.
The routine returns OK if no error occurred while acquiring the block limits;
otherwise, it returns ERROR.
188
4
I/O System
The sequential device driver must provide a load or unload routine in order to
mount or unmount tape volumes from a physical tape device. Loading means that
a volume is being mounted by the file system. This is usually done with an open( )
or a creat( ) call. However, a device should be unloaded or unmounted only when
the file system wants to eject the tape volume from the tape device.
The load/unload routine is defined as follows:
STATUS xxLoad
(
DEVICE * pDev,
BOOL
load
)
pDev
load
The sequential device driver must provide a space routine that moves, or spaces,
the tape medium forward or backward. The amount of distance that the tape
spaces depends on the kind of search that must be performed. In general, tapes can
be searched by end-of-record marks, end-of-file marks, or other types of
device-specific markers.
The basic definition of the space routine is as follows; however, other arguments
can be added to the definition:
STATUS xxSpace
(
DEVICE * pDev,
int
count,
int
spaceCode
)
189
VxWorks 5.5
Programmers Guide
pDev
count
spaceCode
defines the type of space mark that the tape device searches for on the
tape medium. The basic types of space marks are end-of-record and
end-of-file. However, different tape devices may support more
sophisticated kinds of space marks designed for more efficient
maneuvering of the medium by the tape device.
If the device is able to space in the specified direction by the specified count and
space code, the routine returns OK; if these conditions cannot be met, it returns
ERROR.
The sequential driver must provide a routine that allows a tape to be erased. The
erase routine is defined as follows:
STATUS xxErase
(
DEVICE * pDev
)
190
4
I/O System
Table 4-17
Description
errnoLib
ftpLib
ioLib
iosLib
intLib
remLib
rngLib
ttyDrv
Terminal driver
wdLib
4.10 PCMCIA
A PCMCIA card can be plugged into notebook computers to connect devices such
as modems and external hard drives.2 VxWorks provides PCMCIA facilities for
pcPentium, pcPentium2, pcPentium3 and BSPs and PCMCIA drivers that allow
VxWorks running on these targets to support PCMCIA hardware.
PCMCIA support is at the PCMCIA Release 2.1 level. It does not include socket
services or card services, which are not required by VxWorks. It does include chip
drivers and libraries. The PCMCIA libraries and drivers are also available in
source code form for VxWorks systems based on CPU architectures other than Intel
Pentium.
To include PCMCIA support in your system, add the INCLUDE_PCMCIA
component to the VxWorks kernel protection domain. For information about
PCMCIA facilities, see the entries for pcmciaLib and pcmciaShow in the VxWorks
API Reference.
2. PCMCIA stands for Personal Computer Memory Card International Association, and refers
to both the association and the standards that it has developed.
191
VxWorks 5.5
Programmers Guide
192
5
Local File Systems
5.1 Introduction
VxWorks uses a standard I/O interface between the file system and the device
driver. This allows multiple file systems, of the same or different types, to operate
within a single VxWorks system. By following these standard interfaces, you can
write your own file system for VxWorks, and freely mix file systems and device
drivers.
This chapter discusses the VxWorks file systems, listed below, describing how they
are organized, configured, and used.
dosFs. Designed for real-time use of block devices (disks) and compatible
rawFS. Provides a simple raw file system that essentially treats an entire disk as
tapeFs. Designed for tape devices that do not use a standard file or directory
structure on tape. Essentially treats the tape volume as a raw device in which
the entire volume is a large file.
TSFS (Target Server File System) . Uses the Tornado target server to provide
193
VxWorks 5.5
Programmers Guide
Support for VFAT (Microsoft VFAT long file names) and VXLONGS (VxWorks
long file names) directory formats.
Support for FAT12, FAT16, and FAT32 file allocation table types.
For API reference information about dosFs, see the entries for dosFsLib and
dosFsFmtLib, as well as the cbioLib, dcacheCbio, and dpartCbio entries, in the
VxWorks API Reference.
For information about the MS-DOS file system, please see the Microsoft
documentation.
NOTE: The discussion in this chapter of the dosFs file system uses the term sector
to refer to the minimum addressable unit on a disk. This definition of the term
follows most MS-DOS documentation. However, in VxWorks, these units on the
disk are normally referred to as blocks, and a disk device is called a block device.
194
5
Local File Systems
Step 1:
Configure your kernel with the dosFs, CBIO, and block device components. This
step is described in 5.2.2 Configuring Your System, p.197.
Step 2:
This step is done automatically if you have included the required components in
your project. This step is described in 5.2.3 Initializing the dosFs File System, p.198
Step 3:
Create either a block device or a CBIO driver device (ramDiskCbio). This step is
described in 5.2.4 Creating a Block Device, p.198.
Step 4:
Creating a disk cache is optional. Disk cache is intended only for rotational media.
This step is described in 5.2.5 Creating a Disk Cache, p.198.
Step 5:
Create the dosFs device. You can safely create the device whether or not you are
using a pre-formatted disk. This step is described in 5.2.7 Creating a dosFs Device,
p.201.
Step 7:
If you are not using pre-formatted disks, format the volumes. This step is described
in 5.2.8 Formatting the Volume, p.201.
Step 8:
Optionally, check the disk for volume integrity using dosFsChkDsk( ). Disk
checking large disks can be time-consuming. The parameters you pass to
dosFsDevCreate( ) determine whether disk checking happens automatically. For
details, see the entry for dosFsDevCreate( ) in the VxWorks Reference Manual.
Step 9:
195
VxWorks 5.5
Programmers Guide
Figure 5-1
Application
I/O System
CBIO Interface
CBIO Disk Partitions and/or CBIO Disk Cache
Block Device
SCSI, ATA, RAM disk, Floppy, TrueFFS, and so on
Physical Device
Hardware
196
5
Local File Systems
In addition, you need to include the appropriate component for your block device;
for example, INCLUDE_SCSI or INCLUDE_ATA. Finally, add any related
components that are required for your particular system.
Optional dosFs Components
xcopy, and so on
197
VxWorks 5.5
Programmers Guide
198
5
Local File Systems
Example 5-1
This example takes a pointer to a block device, creates three partitions, creates the
partition handler for these partitions, and creates the dosFs device handler for
them. Then, it formats the partitions using dosFsVolFormat( ), which is discussed
in the next section.
STATUS usrPartDiskFsInit
(
void * blkDevId
/* CBIO_DEV_ID or BLK_DEV*
)
{
const char * devNames[] = { "/sd0a", "/sd0b", "/sd0c" };
int dcacheSize = 0x30000 ;
CBIO_DEV_ID cbio, cbio1 ;
/* create disk cache */
5
*/
199
VxWorks 5.5
Programmers Guide
The following example configures a partitioned disk with three already existing
partitions. Note that the ATA hard disk component allows for auto-mounting as
many partitions as are referenced within its name parameter.
STATUS usrPartDiskFsInit
(
void * blkDevId
/* CBIO_DEV_ID or BLK_DEV*/
)
{
const char * devNames[] = { "/sd0a", "/sd0b", "/sd0c" };
int dcacheSize = 0x30000 ;
CBIO_DEV_ID cbio, cbio1 ;
/* create disk cache */
if((cbio = dcacheDevCreate(blkDevId, NULL, dcacheSize, "/sd0"))
== NULL )
return ERROR ;
/* create partition manager with FDISK style decoder, up to 3 parts */
if((cbio1 = dpartDevCreate( cbio, 3, usrFdiskPartRead )) == NULL)
return ERROR;
/* create the 1st file system, 8 simultaneously open files
* with CHKDSK
*/
if(dosFsDevCreate( devNames[0], dpartPartGet(cbio1,0), 8, 0 )
== ERROR)
return ERROR;
/* create the 2nd file sys, 6 simultaneously open files, with CHKDSK */
if(dosFsDevCreate( devNames[1], dpartPartGet(cbio1,1), 6, 0 ) == ERROR)
return ERROR;
/* create the 3rd file system, 4 simultaneously open files, no CHKDSK */
if(dosFsDevCreate( devNames[2], dpartPartGet(cbio1,2), 4, 0)
==ERROR)
200
5
Local File Systems
return ERROR;
return OK;
}
For more details, see the VxWorks API Reference entries for dosFsVolFormat( ) and
ioctl( ).
The MS-DOS and dosFs file systems provide options for the format of the File
Allocation Table (FAT) and the format of the directory. These options, described
below, are completely independent.
!
CAUTION: If you are using a disk that is already initialized with an MS-DOS boot
sector, FAT, and root directoryfor example, by using the FORMAT utility in
MS-DOSyou can use dosFsDevCreate( ) to create a dosFs device. However, do
not call dosFsVolFormat( ) or the file system data structures will be re-initialized
(reformatted).
201
VxWorks 5.5
Programmers Guide
A volume FAT format is set during disk formatting, according to either the volume
size (by default), or the per-user defined settings passed to dosFsVolFormat( ). FAT
options are summarized in Table 5-1:
Table 5-1
FAT Formats
Format
Usage
Size
FAT12
FAT16
FAT32
Directory Formats
There are three options for the directory format. These are:
MSFT Long Names (VFAT) Uses case-insensitive long filenames, with up to 254
characters. This format accepts disks created with short names. MSFT Long
Names1 is the default directory format.
uppercase characters for the name itself and three for the extension.
VxWorks Long Names (VxLong) Wind Rivers proprietary VxWorks long name
support, introduced prior to MSFT Long Names and used for backward
compatibility with old dosFs VxLong disks. Allows case-sensitive filenames of up
to 40 characters (consisting of any ASCII characters). The dot character (.), which
indicates a file-name extension in MS-DOS, has no special significance in dosFs
VxLong.
NOTE: The VxWorks long names format supports 40-bit file size fields, allowing
1. The MSFT Long Names (VFAT) format supports 32-bit file size fields, limiting the file size
to a 4 GB maximum.
202
5
Local File Systems
WARNING: If you use VxWorks Long Names, the disk will not be MS-DOS
compatible. Use this long name support only for storing data local to VxWorks, on
a disk that is initialized on a VxWorks system.
unformatted or a removable diskette is not inserted in the drive at the time the
system boots. A system can boot successfully and initialize all its devices even if a
drive has no removable media in it and the medias configuration and parameters
are unknown.
CAUTION: Because device names are recognized by the I/O system using simple
substring matching, file systems should not use a slash (/) alone as a name;
unexpected results may occur.
Example 5-3
This example demonstrates how to initialize an ATA disk with dosFs2. This
example displays the commands and output from the VxWorks shell. While these
steps use an ATA block device type, they are applicable to other block devices.
203
VxWorks 5.5
Programmers Guide
Step 1:
Create a block device (BLK_DEV) that controls the master ATA hard disk (drive
zero) on the primary ATA controller (controller zero). This block device uses the
entire disk.
-> pAta = ataDevCreate (0,0,0,0)
new symbol "pAta" added to symbol table.
pAta = 0x3fff334: value = 67105604 = 0x3fff344 = pAta + 0x10
Above, pAta is now a block device pointer (BLK_DEV *). The routine
ataDevCreate( ) returns a valid value. A return value of NULL (0x0) indicates an
error in ataDevCreate( ). Such an error usually indicates a BSP configuration or
hardware configuration error.
This step is appropriate for any BLK_DEV device; for example, flash, SCSI, and so
on. For related information, see the reference entry for ataDevCreate( ).
Step 2:
Next, create an (optional) disk data cache CBIO layer atop this disc:
-> pCbioCache = dcacheDevCreate (pAta,0,0,"ATA Hard Disk Cache")
new symbol "pCbioCache" added to symbol table.
pCbioCache = 0x3ffdbd0: value = 67105240 = 0x3fff1d8
Create Partitions
Then, create two partitions on this disk device, specifying 50% of the disk space for
the second partition, leaving 50% for the first partition. This step should only be
performed once, when the disk is first initialized. If partitions are already written
to the disk, this step should not be performed since it destroys data:
-> usrFdiskPartCreate (pCbioCache, 2, 50, 0, 0)
value = 0 = 0x0
For more information, see the entry for usrFdiskPartLibCbio( ) in the VxWorks
Reference Manual.
In this step, the block device pointer pAta could have been passed instead of
pCbioCache. Doing so would cause usrFdiskPartCreate( ) to use the BLK_DEV
routines via a wrapper internally created by cbioWrapBlkDev( ).
204
5
Local File Systems
Step 4:
Display Partitions
Now, you can optionally display the partitions created with usrFdiskPartShow( ):
-> usrFdiskPartShow (pAta)
Master Boot Record - Partition Table
-------------------------------------Partition Entry number 00
Partition Entry offset 0x1be
Status field = 0x80
Primary (bootable) Partition
Type 0x06: MSDOS 16-bit FAT >=32M Partition
Partition start LCHS: Cylinder 0000, Head 001, Sector 01
Partition end
LCHS: Cylinder 0245, Head 017, Sector 39
Sectors offset from MBR partition 0x00000027
Number of sectors in partition 0x00262c17
Sectors offset from start of disk 0x00000027
Master Boot Record - Partition Table
-------------------------------------Partition Entry number 01
Partition Entry offset 0x1ce
Status field = 0x00
Non-bootable Partition
Type 0x06: MSDOS 16-bit FAT >=32M Partition
Partition start LCHS: Cylinder 0000, Head 018, Sector 01
Partition end
LCHS: Cylinder 0233, Head 067, Sector 39
Sectors offset from MBR partition 0x00262c3e
Number of sectors in partition 0x00261d9e
Sectors offset from start of disk 0x00262c3e
Master Boot Record - Partition Table
-------------------------------------Partition Entry number 02
Partition Entry offset 0x1de
Status field = 0x00
Non-bootable Partition
Type 0x00: Empty (NULL) Partition
Master Boot Record - Partition Table
-------------------------------------Partition Entry number 03
Partition Entry offset 0x1ee
Status field = 0x00
Non-bootable Partition
Type 0x00: Empty (NULL) Partition
value = 0 = 0x0
->
Note above that two partitions have been created upon the disc, and the remaining
two partition table entries are blank.
NOTE: The CBIO device ID pCbioCache could have been passed to
205
VxWorks 5.5
Programmers Guide
Step 5:
Next, create a partition handler/mounter upon this disk. When using a disk cache
layer, the partition handler code should always be instantiated after the disk cache
layer:
-> pCbioParts = dpartDevCreate (pCbioCache,2, usrFdiskPartRead)
new symbol "pCbioParts" added to symbol table.
pCbioParts = 0x3ffd92c: value = 67099276 = 0x3ffda8c = pCbioParts + 0x160
->
The call to dpartDevCreate( ) informs the partition layer to expect two partitions
on the disk (24 is the maximum number of partitions the mounter can handle.) We
have also instructed the partition manager (dpartCbio) to use the FDISK style
partition mounter code usrFdiskPartRead( ).
For more information, see the reference entry for dpartDevCreate( ).
Step 6:
Create dosFs2 file systems atop each partition. The last argument specifies the
integrated chkdsk configuration.
-> dosFsDevCreate ("/DOSA", dpartPartGet (pCbioParts,0), 16, 0)
value = 0 = 0x0
-> dosFsDevCreate ("/DOSB", dpartPartGet (pCbioParts,1), 16, -1)
value = 0 = 0x0
->
-> devs
drv name
0 /null
1 /tyCo/0
1 /tyCo/1
6 ahostname:
3 /DOSA
<---- First Partition
3 /DOSB
<---- Second Partition
value = 25 = 0x19
->
This step defines the volume parameters and adds them to the IO system; it does
not format the volumes. No disk I/O is performed during this step. The volumes
are not mounted at this stage.
For more information, see the entry for dosFsLib in the VxWorks Reference Manual.
206
5
Local File Systems
Step 7:
Next, format the DOS volumes. This step need only be done once, when the
volumes are first initialized. If the DOS volumes have already been initialized
(formatted), then omit this step. The example formats the file system volumes with
default options:
-> dosFsVolFormat ("/DOSA",0,0)
Retrieved old volume params with %100 confidence:
Volume Parameters: FAT type: FAT16, sectors per cluster 32
2 FAT copies, 0 clusters, 153 sectors per FAT
Sectors reserved 1, hidden 39, FAT sectors 306
Root dir entries 512, sysId (null) , serial number 8120000
Label:"
" ...
Disk with 2501655 sectors of 512 bytes will be formatted with:
Volume Parameters: FAT type: FAT16, sectors per cluster 64
2 FAT copies, 39082 clusters, 153 sectors per FAT
Sectors reserved 1, hidden 39, FAT sectors 306
Root dir entries 512, sysId VXDOS16 , serial number 8120000
Label:"
" ...
value = 0 = 0x0
-> dosFsVolFormat ("/DOSB",0,0)
Retrieved old volume params with %100 confidence:
Volume Parameters: FAT type: FAT16, sectors per cluster 32
2 FAT copies, 0 clusters, 153 sectors per FAT
Sectors reserved 1, hidden 39, FAT sectors 306
Root dir entries 512, sysId (null) , serial number 9560000
Label:"
" ...
Disk with 2497950 sectors of 512 bytes will be formatted with:
Volume Parameters: FAT type: FAT16, sectors per cluster 64
2 FAT copies, 39024 clusters, 153 sectors per FAT
Sectors reserved 1, hidden 39, FAT sectors 306
Root dir entries 512, sysId VXDOS16 , serial number 9560000
Label:"
" ...
value = 0 = 0x0
->
For more information, see the entry for dosFsFmtLib in the VxWorks Reference
Manual.
Step 8:
Now, the dosFs volumes are ready to access. Note that /DOSA will start a default
chkdsk code and that /DOSB will not. Autochk is set via the fourth argument to
dosFsDevCreate( ).
-> ll "/DOSA"
/DOSA/ - disk check in progress ...
/DOSA/ - Volume is OK
207
VxWorks 5.5
Programmers Guide
total # of clusters:
# of free clusters:
# of bad clusters:
total free space:
max contiguous free space:
# of files:
# of folders:
total bytes in files:
# of lost chains:
total bytes in lost chains:
39,085
39,083
0
1,221 Mb
1,280,671,744 bytes
0
0
0
0
0
0x3f367c0
0x3f37be0
NOT ENABLED
18
0
0
0
0
208
4 clusters
1,278,771,200 bytes
5
Local File Systems
Above, we can see the Volume parameters for the /DOSB volume. The file system
volumes are now mounted and ready to be exercised.
If you are working with an ATA hard disk or a CD-ROM file system from an ATAPI
CD-ROM drive, you can, alternatively, use usrAtaConfig( ). This routine processes
several steps at once. For more information, see the reference entry.
Example 5-4
The following example creates a RAM disk of a certain size, and formats it for use
with the dosFs file system. This example uses the ramDiskCbio module, which is
intended for direct use with dosFsLib:
STATUS usrRamDiskInit
(
void
)
{
int ramDiskSize = 128 * 1024 ;
char *ramDiskDevName = "/ram0" ;
CBIO_DEV_ID cbio ;
/* no argument */
This example initializes a SCSI disk as a single file system volume (and assumes
that the disk is already formatted).
STATUS usrScsiDiskInit
(
int scsiId
)
{
/* SCSI id */
209
VxWorks 5.5
Programmers Guide
/*
/*
/*
/*
/*
210
5
Local File Systems
You can inform dosFsLib that a disk change is taking place by using the
ready-change mechanism. A change in the disks ready-status is interpreted by
dosFsLib as indicating that the disk must be remounted before the next I/O
operation. To announce a ready-change, use any of the following methods:
5
Have the device driver set the bd_readyChanged field in the BLK_DEV
structure to TRUE; this has the same effect as notifying dosFsLib directly.
Synchronizing Volumes
When a disk is synchronized, all modified buffered data is physically written to the
disk, so that the disk is up to date. This includes data written to files, updated
directory information, and the FAT. To avoid loss of data, a disk should be
synchronized before it is removed. For more information, see the entries for
close( ) and dosFsVolUnmount( ) in the VxWorks Reference Manual.
Creating Subdirectories
For FAT32, subdirectories can be created in any directory at any time. For FAT12
and FAT16, subdirectories can be created in any directory at any time, except in the
root directory once it reaches its maximum entry count. Subdirectories can be
created in the following ways:
211
VxWorks 5.5
Programmers Guide
1.
Using ioctl( ) with the FIOMKDIR function: The name of the directory to be
created is passed as a parameter to ioctl( ).
2.
Using open( ): To create a directory, the O_CREAT option must be set in the
flags parameter to open, and the FSTAT_DIR option must be set in the mode
parameter. The open( ) call returns a file descriptor that describes the new
directory. Use this file descriptor for reading only and close it when it is no
longer needed.
3.
When creating a directory using any of the above methods, the new directory name
must be specified. This name can be either a full pathname or a pathname relative
to the current working directory.
Removing Subdirectories
A directory that is to be deleted must be empty (except for the . and .. entries).
The root directory can never be deleted. Subdirectories can be removed in the
following ways:
1.
Using ioctl( ) with the FIORMDIR function, specifying the name of the
directory. Again, the file descriptor used can refer to any file or directory on the
volume, or to the entire volume itself.
2.
3.
212
5
Local File Systems
File I/O
Files on a dosFs file system device are created, deleted, written, and read using the
standard VxWorks I/O routines: creat( ), remove( ), write( ), and read( ). For more
information, see 4.3 Basic I/O, p.111, and the ioLib entries in the VxWorks API
Reference.
File Attributes
The file-attribute byte in a dosFs directory entry consists of a set of flag bits, each
indicating a particular file characteristic. The characteristics described by the
file-attribute byte are shown in Table 5-2.
Table 5-2
Hex Value
Description
DOS_ATTR_RDONLY
0x01
read-only file
DOS_ATTR_HIDDEN
0x02
hidden file
DOS_ATTR_SYSTEM
0x04
system file
DOS_ATTR_VOL_LABEL
0x08
volume label
DOS_ATTR_DIRECTORY
0x10
subdirectory
DOS_ATTR_ARCHIVE
0x20
DOS_ATTR_RDONLY
If this flag is set, files accessed with open( ) cannot be written to. If the
O_WRONLY or O_RDWR flags are set, open( ) returns ERROR, setting errno to
S_dosFsLib_READ_ONLY.
DOS_ATTR_HIDDEN
213
VxWorks 5.5
Programmers Guide
DOS_ATTR_SYSTEM
This is a volume label flag, which indicates that a directory entry contains the
dosFs volume label for the disk. A label is not required. If used, there can be
only one volume label entry per volume, in the root directory. The volume
label entry is not reported when reading the contents of a directory (using
readdir( )). It can only be determined using the ioctl( ) function FIOLABELGET.
The volume label can be set (or reset) to any string of 11 or fewer characters,
using the ioctl( ) function FIOLABELSET. Any file descriptor open to the
volume can be used during these ioctl( ) calls.
DOS_ATTR_DIRECTORY
This is a directory flag, which indicates that this entry is a subdirectory, and not
a regular file.
DOS_ATTR_ARCHIVE
This is an archive flag, which is set when a file is created or modified. This flag
is intended for use by other programs that search a volume for modified files
and selectively archive them. Such a program must clear the archive flag, since
VxWorks does not.
All the flags in the attribute byte, except the directory and volume label flags, can
be set or cleared using the ioctl( ) function FIOATTRIBSET. This function is called
after the opening of the specific file with the attributes to be changed. The
attribute-byte value specified in the FIOATTRIBSET call is copied directly; to
preserve existing flag settings, determine the current attributes using stat( ) or
fstat( ), then change them using bitwise AND and OR operations.
Example 5-6
This example makes a dosFs file read-only, and leaves other attributes intact.
STATUS changeAttributes
(
void
)
{
int
fd;
struct stat
statStruct;
/* open file */
if ((fd = open ("file", O_RDONLY, 0)) == ERROR)
return (ERROR);
214
5
Local File Systems
NOTE: You can also use the attrib( ) routine to change file attributes. For more
single cluster allocation. Single cluster allocation uses a single cluster, which is
the minimum allocation unit. This method is automatically used when the
write operation is smaller than the size of a single cluster.
For any allocation method, you can deallocate unused reserved bytes by using the
POSIX-compatible routine ftruncate( ) or the ioctl( ) function FIOTRUNC.
215
VxWorks 5.5
Programmers Guide
The dosFs file system defines the size of a cluster group based on the medias
physical characteristics. That size is fixed for each particular media. Since seek
operations are an overhead that reduces performance, it is desirable to arrange files
so that sequential portions of a file are located in physically contiguous disk
clusters. Cluster group allocation occurs when the cluster group size is considered
sufficiently large so that the seek time is negligible compared to the read/write
time. This technique is sometimes referred to as nearly contiguous file access
because seek time between consecutive cluster groups is significantly reduced.
Because all large files on a volume are expected to have been written as a group of
extents, removing them frees a number of extents to be used for new files
subsequently created. Therefore, as long as free space is available for subsequent
file storage, there are always extents available for use. Thus, cluster group
allocation effectively prevents fragmentation (where a file is allocated in small units
spread across distant locations on the disk). Access to fragmented files can be
extremely slow, depending upon the degree of fragmentation.
216
5
Local File Systems
First, create the file in the normal fashion using open( ) or creat( ).
2.
Then, call ioctl( ). Use the file descriptor returned from open( ) or creat( ) as the
file descriptor argument. Specify FIOCONTIG as the function code argument
and the size of the requested contiguous area, in bytes, as the third argument.
The FAT is then searched for a suitable section of the disk. If found, this space is
assigned to the new file. The file can then be closed, or it can be used for further
I/O operations. The file descriptor used for calling ioctl( ) should be the only
descriptor open to the file. Always perform the ioctl( ) FIOCONTIG operation
before writing any data to the file.
To request the largest available contiguous space, use CONTIG_MAX for the size of
the contiguous area. For example:
status = ioctl (fd, FIOCONTIG, CONTIG_MAX);
Subdirectories can also be allocated a contiguous disk area in the same manner:
If the directory is created using options to open( ), the returned file descriptor
from that call can be used.
A directory must be empty (except for the . and .. entries) when it has
contiguous space allocated to it.
217
VxWorks 5.5
Programmers Guide
Fragmented files require following cluster chains in the FAT. However, if a file is
recognized as contiguous, the system can use an enhanced method that improves
performance. This applies to all contiguous files, whether or not they were
explicitly created using FIOCONTIG. Whenever a file is opened, it is checked for
contiguity. If it is found to be contiguous, the file system registers the necessary
information about that file to avoid the need for subsequent access to the FAT table.
This enhances performance when working with the file by eliminating seek
operations.
When you are opening a contiguous file, you can explicitly indicate that the file is
contiguous by specifying the DOS_O_CONTIG_CHK flag with open( ). This
prompts the file system to retrieve the section of contiguous space, allocated for
this file, from the FAT table.
Demonstrating with an Example
To find the maximum contiguous area on a device, you can use the ioctl( ) function
FIONCONTIG. This information can also be displayed by dosFsConfigShow( ).
Example 5-7
In this example, the size (in bytes) of the largest contiguous area is copied to the
integer pointed to by the third parameter to ioctl( ) (count).
STATUS contigTest
(
void
)
{
int count;
int fd;
/* no argument */
218
5
Local File Systems
The inconsistencies occur because the file system data for a single file is stored in
three separate regions of the disk. The data stored in these regions are:
The file chain in the File Allocation Table (FAT), located in a region near the
beginning of the disk.
Since all three regions cannot be always updated before an interruption, dosFs
includes an optional integrated consistency-checking mechanism to detect and
recover from inconsistencies. For example, if a disk is removed when a file is being
deleted, a consistency check completes the file deletion operation. Or, if a file is
being created when an interruption occurs, then the file is un-created. In other
words, the consistency checker either rolls forward or rolls back the operation that
has the inconsistency, making whichever correction is possible.
To use consistency checking, specify the autoChkLevel parameter to
dosFsDevCreate( ), invoke it manually, or call the chkdsk( ) utility. If configured,
consistency checking is invoked under the following conditions:
first accessed.
219
VxWorks 5.5
Programmers Guide
For more information, see the manual entries for dosFsLib and for ioctl( ) in ioLib.
Table 5-3
Decimal
Description
Value
FIOATTRIBSET
35
FIOCONTIG
36
FIODISKCHANGE
13
FIODISKFORMAT
FIODISKINIT
FIOFLUSH
FIOFSTATGET
38
FIOGETNAME
18
FIOLABELGET
33
FIOLABELSET
34
FIOMKDIR
31
FIOMOVE
47
FIONCONTIG
41
FIONFREE
30
FIONREAD
FIOREADDIR
37
FIORENAME
10
FIORMDIR
32
Removes a directory.
FIOSEEK
FIOSYNC
21
FIOTRUNC
42
FIOUNMOUNT
39
FIOWHERE
220
5
Local File Systems
Step 1:
WARNING: For use as a boot device, the directory name for the dosFs file system
must begin and end with slashes (as with /sd0/ used in the following example).
This is an exception to the usual naming convention for dosFs file systems and is
incompatible with the NFS requirement that device names not end in a slash.
Create the SCSI Device
Create the SCSI device using scsiPhysDevCreate( ) (see SCSI Drivers, p.146), and
initialize the disk with a dosFs file system (see 5.2.3 Initializing the dosFs File System,
p.198). Modify the file installDir/target/bspName/sysScsi.c to reflect your SCSI
configuration.
Step 2:
Copy the file vxWorks to the drive. Below, a VxWorks task spawns the copy( )
routine, passing it two arguments.
The first argument is the source file for the copy( ) command. The source file is the
VxWorks runtime image, vxWorks. The source host name is tiamat:, the source
filename is C:/vxWorks. These are passed to copy( ) in concatenated form, as the
string tiamat:C:/vxWorks.
The second argument is the destination file for the copy( ) command. The dosFs
file system, on the local target SCSI disk device, is named /sd0, and the target file
name is vxWorks. These are, similarly, passed to copy( ) in concatenated form, as
the string /sd0/vxWorks. When booting the target from the SCSI device, the
bootrom image should specify the runtime file as /sd0/vxWorks.
221
VxWorks 5.5
Programmers Guide
Step 4:
Depending upon image configuration, the vxWorks.sym file for the system
symbol table may also be needed. Therefore, in similar fashion, copy the
vxWorks.sym file. The runtime image, vxWorks, downloads the vxWorks.sym file
from the same location.
-> sp (copy, "tiamat:c:/vxWorks.sym","/sd0/vxWorks.sym")
task spawned: id = 0x3f2a1bc, name = t3
value = 66232764 = 0x3f2a1bc
Copy OK: 147698 bytes copied
Step 5:
Now, list the files to ensure that the files were correctly copied.
-> sp (ll, "/sd0")
task spawned: id = 0x3f2a1a8, name = t4
value = 66232744 = 0x3f2a1a8
->
Listing Directory /sd0:
-rwxrwxrwx 1 0
0
-rwxrwxrwx 1 0
0
Step 6:
Reboot the system, and then change the boot parameters. Boot device parameters
for SCSI devices follow this format:
scsi=id,lun
where id is the SCSI ID of the boot device, and lun is its Logical Unit Number
(LUN). To enable use of the network, include the on-board Ethernet device (for
example, ln for LANCE) in the other field.
The following example boots from a SCSI device with a SCSI ID of 2 and a LUN of
0.
boot device
processor number
host name
file name
inet on ethernet (e)
host inet (h)
222
:
:
:
:
:
:
scsi=2,0
0
host
/sd0/vxWorks
147.11.1.222:ffffff00
147.11.1.3
5
Local File Systems
user (u)
flags (f)
target name (tn)
other
:
:
:
:
jane
0x0
t222
ln
223
VxWorks 5.5
Programmers Guide
The rawFsInit( ) routine also makes an entry for the rawFs file system in the I/O
system driver table (with iosDrvInstall( )). This entry specifies the entry points for
rawFs file operations, for all devices that use the rawFs file system. The driver
number assigned to the rawFs file system is placed in a global variable,
rawFsDrvNum.
The rawFsInit( ) routine is normally called by the usrRoot( ) task after starting the
VxWorks system.
The rawFsDevInit( ) call assigns the specified name to the device and enters the
device in the I/O system device table (with iosDevAdd( )). It also allocates and
initializes the file systems volume descriptor for the device. It returns a pointer to
the volume descriptor to the caller; this pointer is used to identify the volume
during certain file system calls.
Note that initializing the device for use with rawFs does not format the disk. That
is done using an ioctl( ) call with the FIODISKFORMAT function.
NOTE: No disk initialization (FIODISKINIT) is required, because there are no file
system structures on the disk. Note, however, that rawFs accepts that ioctl( )
function code for compatibility with other file systems; in such cases, it performs
no action and always returns OK.
224
5
Local File Systems
CAUTION: Because device names are recognized by the I/O system using simple
substring matching, file systems should not use a slash (/) alone as a name;
unexpected results may occur.
Un-mounting Volumes
225
VxWorks 5.5
Programmers Guide
descriptors as obsolete. The next I/O operation remounts the disk. Calling ioctl( )
with FIOUNMOUNT is equivalent to using rawFsVolUnmount( ). Any open file
descriptor to the device can be used in the ioctl( ) call.
Attempts to use obsolete file descriptors for further I/O operations produce an
S_rawFsLib_FD_OBSOLETE error. To free an obsolete descriptor, use close( ), as
usual. This frees the descriptor even though it produces the same error.
ISRs must not call rawFsVolUnmount( ) directly, because the call can pend while
the device becomes available. The ISR can instead give a semaphore that prompts
a task to un-mount the volume. (Note that rawFsReadyChange( ) can be called
directly from ISRs; see Announcing Disk Changes with Ready-Change, p.226.)
When rawFsVolUnmount( ) is called, it attempts to write buffered data out to the
disk. Its use is therefore inappropriate for situations where the disk-change
notification does not occur until a new disk is inserted, because the old buffered
data would be written to the new disk. In this case, use rawFsReadyChange( ), as
described in Announcing Disk Changes with Ready-Change, p.226.
If rawFsVolUnmount( ) is called after the disk is physically removed, the data
flushing portion of its operation fails. However, the file descriptors are still marked
as obsolete, and the disk is marked as requiring remounting. An error is not
returned by rawFsVolUnmount( ); to avoid lost data in this situation, explicitly
synchronize the disk before removing it (see Synchronizing Volumes, p.227).
The second method of announcing that a disk change is taking place is with the
ready-change mechanism. A change in the disks ready-status is interpreted by
rawFsLib to indicate that the disk must be remounted during the next I/O call.
There are three ways to announce a ready-change:
By having the device driver set the bd_readyChanged field in the BLK_DEV
structure to TRUE; this has the same effect as notifying rawFsLib directly.
The ready-change announcement does not cause buffered data to be flushed to the
disk. It merely marks the volume as needing remounting. As a result, data written
to files can be lost. This can be avoided by synchronizing the disk before asserting
ready-change. The combination of synchronizing and asserting ready-change
226
5
Local File Systems
Synchronizing Volumes
When a disk is synchronized, all buffered data that is modified is written to the
physical device so that the disk is up to date. For the rawFs file system, the only
such data is that contained in open file descriptor buffers.
To avoid loss of data, synchronize a disk before removing it. You may need to
explicitly synchronize a disk, depending on when (or if) the rawFsVolUnmount( )
call is issued.
When rawFsVolUnmount( ) is called, an attempt is made to synchronize the
device before un-mounting. If this disk is still present and writable at the time of
the call, synchronization takes place automatically; there is no need to synchronize
the disk explicitly.
However, if the rawFsVolUnmount( ) call is made after a disk is removed, it is
obviously too late to synchronize, and rawFsVolUnmount( ) discards the buffered
data. Therefore, make a separate ioctl( ) call with the FIOSYNC function before
removing the disk. (For example, this could be done in response to an operator
command.) Any open file descriptor to the device can be used during the ioctl( )
call. This call writes all modified file descriptor buffers for the device out to the
disk.
227
VxWorks 5.5
Programmers Guide
Table 5-4
Decimal
Value
Description
FIODISKCHANGE
13
FIODISKFORMAT
FIODISKINIT
FIOFLUSH
Same as FIOSYNC.
FIOGETNAME
18
FIONREAD
FIOSEEK
FIOSYNC
21
FIOUNMOUNT
39
FIOWHERE
228
5
Local File Systems
Once the tapeFs file system has been initialized, the next step is to create one or
more devices that can be used with it. This is done using the sequential device
creation routine, scsiSeqDevCreate( ). The driver routine returns a pointer to a
sequential device descriptor structure, SEQ_DEV. The SEQ_DEV structure
describes the physical aspects of the device and specifies the routines in the device
229
VxWorks 5.5
Programmers Guide
driver that tapeFs can call. For more information on sequential devices, see the
manual entry for scsiSeqDevCreate( ), Configuring SCSI Drivers, p.147 and
4.9.4 Block Devices, p.176.
Immediately after its creation, the sequential device has neither a name nor a file
system associated with it. To initialize a sequential device for use with tapeFs, call
tapeFsDevInit( ) to assign a name and declare a file system. Its parameters are the
volume namefor identifying the device; a pointer to SEQ_DEVthe sequential
device descriptor structure; and a pointer to an initialized tape configuration
structure TAPE_CONFIG. This structure has the following form:
typedef struct /* TAPE_CONFIG tape device config structure */
{
int blkSize;
/* block size; 0 => var. block size */
BOOL rewind;
/* TRUE => a rewind device; FALSE => no rewind */
int numFileMarks;
/* not used */
int density;
/* not used */
} TAPE_CONFIG;
In the preceding definition of TAPE_CONFIG, only two fields, blkSize and rewind,
are currently in use. If rewind is TRUE, then a tape device is rewound to the
beginning-of-medium (BOM) upon closing a file with close( ). However, if rewind
is FALSE, then closing a file has no effect on the position of the read/write head on
the tape medium.
The blkSize field specifies the block size of the physical tape device. Having set the
block size, each read or write operation has a transfer unit of blkSize. Tape devices
can perform fixed or variable block transfers, a distinction also captured in the
blkSize field.
For more information on initializing a tapeFs device, see the VxWorks API Reference
entry for tapeFsDevInit( ).
A tape file system can be created for fixed block size transfers or variable block size
transfers, depending on the capabilities of the underlying physical device. The
type of data transfer (fixed block or variable block) is usually decided when the
tape device is being created in the file system, that is, before the call to
tapeFsDevInit( ). A block size of zero represents variable block size data transfers.
Once the block size has been set for a particular tape device, it is usually not
modified. To modify the block size, use the ioctl( ) functions FIOBLKSIZESET and
FIOBLKSIZEGET to set and get the block size on the physical device.
230
5
Local File Systems
Note that for fixed block transfers, the tape file system buffers a block of data. If the
block size of the physical device is changed after a file is opened, the file should
first be closed and then re-opened in order for the new block size to take effect.
Example 5-8
There are many ways to configure a tape device. In this code example, a tape
device is configured with a block size of 512 bytes and the option to rewind the
device at the end of operations.
/* global variables assigned elsewhere */
SCSI_PHYS_DEV *
pScsiPhysDev;
pTapeVol;
pSeqDev;
tapeConfig;
/* initialization code */
tapeConfig.blkSize = 512;
tapeConfig.rewind
= TRUE;
pSeqDev
= scsiSeqDevCreate (pScsiPhysDev);
pTapeVol
= tapeFsDevInit ("/tape1", pSeqDev, tapeConfig);
The tapeFsDevInit( ) call assigns the specified name to the device and enters the
device in the I/O system device table (with iosDevAdd( )). The return value of this
routine is a pointer to a volume descriptor structure that contains volume-specific
configuration and state information.
CAUTION: Because device names are recognized by the I/O system using simple
substring matching, file systems should not use a slash (/) alone as a name;
unexpected results may occur.
The tapeFs tape volumes can be operated in only one of two modes: read-only
(O_RDONLY) or write-only (O_WRONLY). There is no read-write mode. The mode
of operation is defined when the file is opened using open( ).
231
VxWorks 5.5
Programmers Guide
232
5
Local File Systems
Table 5-5
Decimal Value
Description
FIOFLUSH
FIOSYNC
21
Same as FIOFLUSH.
FIOBLKSIZEGET
1001
FIOBLKSIZESET
1000
Sets the block size of the tape device on the device and in
the SEQ_DEV data structure.
MTIOCTOP
1005
The MTIOCTOP operation is compatible with the UNIX MTIOCTOP operation. The
argument passed to ioctl( ) with MTIOCTOP is a pointer to an MTOP structure that
contains the following two fields:
typedef struct mtop
{
short mt_op;
int mt_count;
} MTOP;
/* operation */
/* number of operations */
The mt_op field contains the type of MTIOCTOP operation to perform. These
operations are defined in Table 5-6. The mt_count field contains the number of
times the operation defined in mt_op should be performed.
Table 5-6
MTIOCTOP Operations
Function
Value
Meaning
MTWEOF
MTFSF
MTBSF
MTFSR
MTBSR
MTREW
233
VxWorks 5.5
Programmers Guide
Table 5-6
Value
Meaning
MTOFFL
MTNOP
MTRETEN
MTERASE
MTEOM
10
MTNBSF
11
234
5
Local File Systems
Example 5-9
The example below describes the steps for creating a block device for the CD-ROM,
creating a cdromFsLib device, mounting the filesystem, and accessing the media
in the device.
Step 1:
Based on the type of device, use the appropriate create routine and create a block
device. Following is an example for an ATAPI master device upon the secondary
ATA controller:
-> pBlkd = ataDevCreate(1, 0, 0, 0)
new symbol "pBlkd" added to symbol table.
pBlkd = 0x3fff334: value = 67105604 = 0x3fff344 = pBlkd + 0x10
Step 3:
A block device must already have been created. Call cdromFsDevCreate( ), which
calls iosDrvInstall( ) internally. This enters the appropriate driver routines in the
I/O driver table.
-> cdromFsDevCreate("/cd", pBlkd)
value = 67105456 = 0x3fff2b0
-> devs
drv name
0 /null
1 /tyCo/0
1 /tyCo/1
5 ala-petrient:
6 /vio
7 /cd
value = 25 = 0x19
-> cd "/cd"
value = 0 = 0x0
The cd command changes the current working directory, without performing I/O,
and, therefore, can be called before the media is mounted.
235
VxWorks 5.5
Programmers Guide
Step 4:
0x3fff2b0
/cd
2048
Mount the device in order to access it. Because cdromFs is mounted during the first
open( ) operation, a call to open( ) or any function that uses open( ) will mount the
device. The ls command below both mounts the device and lists its contents.
-> ls "/cd"
/cd/.
/cd/..
/cd/INDEX.HTML;1
/cd/INSTRUCT.HTML;1
/cd/MPF
/cd/README.TXT;1
value = 0 = 0x0
Step 5:
0x3fff2b0
/cd
2048
236
:CD001
:1
:LINUX
:MPF_CD
:611622912 = 583 MB
:298644 = 0x48e94
:1
:1
:2048
:3476
:238
:
5
Local File Systems
volume publisher ID
:WorldWide Technolgies
:Kelly Corday
volume application ID
:mkisofs v1.04
:mkisofs v1.04
:mkisofs v1.04
:mkisofs v1.04
:13.11.1998 14:36:49:00
:13.11.1998 14:36:49:00
:00.00.0000 00:00:00:00
:13.11.1998 14:36:49:00
237
VxWorks 5.5
Programmers Guide
requests to be issued to associate the VIO channel with a particular host file; the
information is contained in the name of the file.
Consider a read( ) call. The driver transmits the ID of the file (previously
established by an open( ) call), the address of the buffer to receive the file data, and
the desired length of the read to the target server. The target server responds by
issuing the equivalent read( ) call on the host and transfers the data read to the
target program. The return value of read( ) and any errno that might arise are also
relayed to the target, so that the file appears to be local in every way.
For detailed information, see the VxWorks API Reference entry for wdbTsfsDrv.
Socket Support
TSFS sockets are operated on in a similar way to other TSFS files, using open( ),
close( ), read( ), write( ), and ioctl( ). To open a TSFS socket, use one of the
following forms of filename:
"TCP:hostIP:port"
"TCP:hostname:port"
The flags and permissions arguments are ignored. The following examples show
how to use these filenames:
fd = open("/tgtsvr/TCP:phobos:6164"0,0)
fd = open("/tgtsvr/TCP:150.50.50.50:6164",0,0)
*/
*/
*/
*/
*/
The result of this open( ) call is to open a TCP socket on the host and connect it to
the target server socket at hostname or hostIP awaiting connections on port. The
resultant socket is non-blocking. Use read( ) and write( ) to read and write to the
TSFS socket. Because the socket is non-blocking, the read( ) call returns
immediately with an error and the appropriate errno if there is no data available
to read from the socket. The ioctl( ) usage specific to TSFS sockets is discussed in
the VxWorks API Reference entry for wdbTsfsDrv. This socket configuration allows
VxWorks to use the socket facility without requiring sockLib and the networking
modules on the target.
238
5
Local File Systems
Error Handling
Errors can arise at various points within TSFS and are reported back to the original
caller on the target, along with an appropriate error code. The error code returned
is the VxWorks errno which most closely matches the error experienced on the
host. If a WDB error is encountered, a WDB error message is returned rather than
a VxWorks errno.
TSFS Configuration
To use the TSFS, your VxWorks-based system must be configured with the
INCLUDE_WDB_TSFS component in the kernel. This creates the /tgtsvr file system.
The target server on the host system must also be configured for TSFS. This
involves assigning a root directory on your host to TSFS (see the discussion of the
target server -R option in Security Considerations, p.239). For example, on a PC host
you could set the TSFS root to c:\myTarget\logs.
Having done so, opening the file /tgtsvr/logFoo on the target causes
c:\myTarget\logs\logFoo to be opened on the host by the target server. A new file
descriptor representing that file is returned to the caller on the target.
Security Considerations
While TSFS has much in common with netDrv, the security considerations are
different. With TSFS, the host file operations are done on behalf of the user that
launched the target server. The user name given to the target as a boot parameter
has no effect. In fact, none of the boot parameters have any effect on the access
privileges of TSFS.
In this environment, it is less clear to the user what the privilege restrictions to
TSFS actually are, since the user ID and host machine that start the target server
may vary from invocation to invocation. By default, any Tornado tool that connects
to a target server which is supporting TSFS has access to any file with the same
authorizations as the user that started that target server. However, the target server
can be locked (with the -L option) to restrict access to the TSFS.
The options which have been added to the target server startup routine to control
target access to host files using TSFS include:
239
VxWorks 5.5
Programmers Guide
entry in the online Tornado Tools Reference. For information about specifying target
server options from the Tornado IDE, see the Tornado Users Reference: Target
Manager.
240
6
Target Tools
6.1 Introduction
The Tornado development system provides a full suite of development tools that
resides and executes on the host machine; this approach conserves target memory
and resources. However, there are many situations in which it is desirable to have
a target-resident shell, a target-resident dynamic object loader, simple
target-resident debug facilities, or a target-resident system symbol table. This
chapter discusses the target-resident facilities in detail.
Some situations in which the target based tools may be particularly useful are:
The target based tools are partially independent so, for instance, the target shell
may be used without the target loader, and vice versa. However, for any of the
other individual tools to be completely functional, the system symbol table is
required.
In some situations, it may also be useful to use both the host-resident development
tools and the target-resident tools at the same time. In this case, additional facilities
are required so that both environments maintain consistent views of the system.
For more information, see 6.4.4 Using the VxWorks System Symbol Table, p.266.
This chapter briefly describes these target-resident facilities, as well as providing
an overview of the most commonly used VxWorks show routines.
241
VxWorks 5.5
Programmers Guide
For the most part, the target-resident facilities work the same as their Tornado host
counterparts. For more information, see the appropriate chapters of the Tornado
Users Guide.
Both shells include a C interpreter; the host shell also provides a Tcl interpreter.
Both shells provide an editing mode.
You can have multiple host shells active for any given target; only one target
shell can be active for a target at any one time.
The host shell allows virtual I/O; the target shell does not.
The host shell is always ready to execute provided that the WDB target agent
is included in the system. The target shell, as well as its associated
target-resident symbol tables and module loader, must be configured into the
VxWorks image by including the appropriate components.
The target shells input and output are directed at the same window by
default, usually a console connected to the boards serial port.1 For the host
shell, these standard I/O streams are not necessarily directed to the same
window as the host shell.
1. Provided that suitable hardware is available, standard input and output can be redirected
once the shell is running with ioGlobalStdSet( ).
242
6
Target Tools
The host shell can perform many control and information functions entirely on
the host, without consuming target resources.
The host shell uses host resources for most functions so that it remains
segregated from the target. This means that the host shell can operate on the
target from the outside. The target shell, however, must act on itself, which
means that there are limitations to what it can do. For example, to make
breakable calls in the target shell, sp( ) must be used. In addition, conflicts in
task priority may occur while using the target shell.
The target shell correctly interprets the tilde operator in pathnames, whereas
the host shell cannot. For example, the following command executed from the
target shell by user panloki would correctly locate /home/panloki/foo.o on
the host system:
-> ld < ~/foo.o
The following expression can be used to return the memory to the target
memory pool (see the memLib reference entry for information on memory
management):
-> free (x)
2. The amount of memory allocated is rounded up to the minimum allocation unit for the
architecture in question, plus the amount for the header for that block of memory.
243
VxWorks 5.5
Programmers Guide
This is because if strings were only temporarily allocated, and a string literal
were passed to a routine being spawned as a task, by the time the task executed
and attempted to access the string, the target shell would have already
released (and possibly even reused) the temporary storage where the string
was held.
After extended development sessions with the target shell, the cumulative
memory used for strings may be noticeable. If this becomes a problem, you
must reboot your target.
The host shell also allocates memory on the target if the string is to be used
there. However, it does not allocate memory on the target for commands that
can be performed at the host level (such as lkup( ), ld( ), and so on).
6
Target Tools
The target shell has its own set of terminal-control characters, unlike the host shell,
which inherits its setting from the host window from which it was invoked.
Table 6-1 lists the target shells terminal-control characters. The first four of these
are defaults that can be mapped to different keys using routines in tyLib (see also
Tty Special Characters, p.133).
Table 6-1
Description
CTRL+C
CTRL+H
CTRL+Q
Resumes output.
CTRL+S
CTRL+U
CTRL+X
ESC
Toggles between input mode and edit mode (vi mode only).
The shell line-editing commands are the same as they are for the host shell.
6.2.4 Loading and Unloading Object Modules from the Target Shell
Object modules can be dynamically loaded into a running VxWorks system with
the module loader. The following is a typical load command from the shell, in
which the user downloads appl.o to the appBucket domain:
[appBucket] -> ld < /home/panloki/appl.o
245
VxWorks 5.5
Programmers Guide
The ld( ) command loads an object module from a file, or from standard input, into
a specified protection domain. External references in the module are resolved
during loading.
Once an application module is loaded into target memory, subroutines in the
module can be invoked directly from the shell, spawned as tasks, connected to an
interrupt, and so on. What can be done with a routine depends on the flags used
to download the object module (visibility of global symbols or visibility of all
symbols). For more information about ld, see the VxWorks API Reference entry for
usrLib.
Modules can be reloaded with reld( ), which unloads the previously loaded
module of the same name before loading the new version. For more information
about reld, see the VxWorks API Reference entry for unldLib.
Undefined symbols can be avoided by loading the modules in the appropriate
order. Linking independent files before download can be used to avoid unresolved
references if there are circular references between them, or if the number of
modules is unwieldy. The static linker ldarch can be used to link interdependent
files, so that they can only be loaded and unloaded as a unit.
Unloading a code module removes its symbols from the targets symbol table,
removes the code module descriptor and section descriptor(s) from the module
list, and removes its section(s) from the memory partition(s) that held them.
For information about features of the target loader and unloader, see
6.3 Target-Resident Loader, p.250.
246
6
Target Tools
consequences of calling the routine. In such cases it is usually possible to abort and
restart the target shell task. This is done by pressing the special target-shell abort
character on the keyboard, CTRL+C by default. This causes the target shell task to
restart execution at its original entry point. Note that the abort key can be changed
to a character other than CTRL+C by calling tyAbortSet( ).
When restarted, the target shell automatically reassigns the system standard input
and output streams to the original assignments they had when the target shell was
first spawned. Thus any target shell redirections are canceled, and any executing
shell scripts are aborted.
The abort facility works only if the following are true:
dbgInit( ) has been called (see 6.2.5 Debugging with the Target Shell, p.246).
excTask( ) is running (see the Tornado Users Guide: Configuration and Build).
The devices abort option is enabled. This is done with an ioctl( ) call, usually
in the root task in usrConfig.c. For information on enabling the target shell
abort character, see tty Options, p.132.
Also, you may occasionally enter an expression that causes the target shell to incur
a fatal error such as a bus/address error or a privilege violation. Such errors
normally result in the suspension of the offending task, which allows further
debugging.
However, when such an error is incurred by the target shell task, VxWorks
automatically restarts the target shell, because further debugging is impossible
without it. Note that for this reason, as well as to allow the use of breakpoints and
single-stepping, it is often useful when debugging to spawn a routine as a task
instead of just calling it directly from the target shell.
When the target shell is aborted for any reason, either because of a fatal error or
because it is aborted from the terminal, a task trace is displayed automatically. This
trace shows where the target shell was executing when it died.
Note that an offending routine can leave portions of the system in a state that may
not be cleared when the target shell is aborted. For instance, the target shell might
have taken a semaphore, which cannot be given automatically as part of the abort.
247
VxWorks 5.5
Programmers Guide
When VxWorks is first booted, the target shells terminal is normally the system
console. You can use telnet to access the target shell from a host over the network
if you select INCLUDE_TELNET for inclusion in the project facility VxWorks view
(see Tornado Users Guide: Projects). Defining INCLUDE_TELNET creates the
tTelnetd task. To access the target shell over the network, enter the following
command from the host (targetname is the name of the target VxWorks system):
% telnet "targetname"
UNIX host systems also use rlogin to provide access to the target shell from the
host. Select INCLUDE_RLOGIN for inclusion in the project facility VxWorks view
to create the tRlogind task. However, note that VxWorks does not support telnet
or rlogin access from the VxWorks system to the host.
A message is printed on the system console indicating that the target shell is being
accessed via telnet or rlogin, and that it is no longer available from its console.
If the target shell is being accessed remotely, typing at the system console has no
effect. The target shell is a single-user systemit allows access either from the
system console or from a single remote login session, but not both simultaneously.
To prevent someone from remotely logging in while you are at the console, use the
routine shellLock( ) as follows:
-> shellLock 1
To make the target shell available again to remote login, enter the following:
-> shellLock 0
To end a remote-login target shell session, call logout( ) from the target shell. To
end an rlogin session, type TILDE and DOT as the only characters on a line:
-> ~.
You can be prompted to enter a login user name and password when accessing
VxWorks remotely:
248
6
Target Tools
To obtain encrypted_password, use the tool vxencrypt on the host system. This tool
prompts you to enter your password, and then displays the encrypted version.
To define a group of login names, include a list of loginUserAdd( ) commands in
a startup script and run the script after the system has been booted. Or include the
list of loginUserAdd( ) commands to the file usrConfig.c, then rebuild VxWorks.
NOTE: The values for the user name and password apply only to remote login into
the VxWorks system. They do not affect network access from VxWorks to a remote
system; See VxWorks Network Programmers Guide: rlogin and telnet, Host Access
Applications.
The remote-login security feature can be disabled at boot time by specifying the
flag bit 0x20 (SYSFLAG_NO_SECURITY) in the flags parameter on the boot line (see
Tornado Getting Started). This feature can also be disabled by deselecting
INCLUDE_SECURITY in the project facility VxWorks view.
249
VxWorks 5.5
Programmers Guide
To remove the demangler from a kernel built with the Tornado project facility
simply remove the C++ symbol demangler component.
To remove the demangler from a kernel built from a BSP (command line build)
define the macro INCLUDE_NO_CPLUS_DEMANGLER in config.h.
Excluding this library will not affect the operation of the Wind River Target Shell
but will reduce the human readability of C++ identifiers and symbols.
250
6
Target Tools
memory; and the unloader, which uninstalls object modules. In addition, the
loader relies on information provided by the system symbol table.
NOTE: The target-resident loader is often confused with the bootloader, which is
used to install the kernel image in memory. Although these two tools perform
similar functions, and share some support code, they are separate entities. The
bootloader loads only complete images, and does not perform relocations.
6
Provides facilities for storing and retrieving symbols. For more information,
see 6.4 Target-Resident Symbol Tables, p.261 and the entry for symLib in the
VxWorks Reference Manual. For information about configuration of the symbol
table, see the 6.4.1 Configuring VxWorks with Symbol Tables, p.262.
INCLUDE_SYM_TBL_INIT
251
VxWorks 5.5
Programmers Guide
CAUTION: If you want to use the target-resident symbol tables and loader in
addition to the Tornado host tools, you must configure VxWorks with the
INCLUDE_SYM_TBL_SYNC component to provide host-target symbol table and
module synchronization. For more information, see 6.4.4 Using the VxWorks System
Symbol Table, p.266.
Description
loadModule( )
loadModuleAt( )
unldByModuleId( )
unldByNameAndPath( )
Note that all of the loader routines can be called directly from the shell or from
code. The shell commands, however, are intended to be used only from the shell, not
from within programs.3 In general, shell commands handle auxiliary operations,
such as opening and closing a file; they also print their results and any error
messages to the console.
Table 6-3
Description
ld( )
reld( )
unld( )
The use of some of these routines and commands is discussed in the following
sections.
3. In future releases, calling shell commands programmatically may not be supported.
252
6
Target Tools
For detailed information, see the loadLib, unldLib, and usrLib entries in the
VxWorks API Reference, as well as 6.3.3 Summary List of Loader Options, p.253.
Option
Hex Value
Description
LOAD_NO_SYMBOLS
0x2
LOAD_LOCAL_SYMBOLS
0x4
Only local (private) symbols from the module are registered in the
system's symbol table. No linkage is possible against this code
module92s public symbols. This option is not very useful by it self, but
is one of the base options for LOAD_ALL_SYMBOLS.
LOAD_GLOBAL_SYMBOLS
0x8
Only global (public) symbols from the module are registered in the
system's symbol table. No linkage is possible against this code
module's private symbols. This is the loader's default when the
loadFlags parameter is left as NULL.
LOAD_ALL_SYMBOLS
0xC
Local and global symbols from the module are registered in the
system's symbol table. This option is useful for debugging.
Table 6-5
Option
HIDDEN_MODULE
Hex Value
0x10
Description
The code module is not visible from the moduleShow( ) routine or the
Tornado tools. This is useful on deployed systems when an automatically
loaded module should not be detectable by the user.
253
VxWorks 5.5
Programmers Guide
Table 6-6
Option
Hex Value
Description
LOAD_COMMON_MATCH_NONE
0x100
LOAD_COMMON_MATCH_USER
0x200
LOAD_COMMON_MATCH_ALL
0x400
Option
UNLD_KEEP_BREAKPOINTS
Hex Value
Description
0x1
254
6
Target Tools
Constructors must be executed before any other code in the code module is
executed. Similarly, destructors must be executed after the code module is no
longer to be used, but before the module is unloaded. In general, executing
constructors at load time and destructors at unload time is the simplest way to
handle them. However, for debugging purposes, a user may want to decouple the
execution of constructors and destructors from the load and unload steps.
For this reason, the behavior of the loader and unloader regarding constructors
and destructors is configurable. When the C++ strategy is set to AUTOMATIC (1),
the constructors and destructors are executed at load and unload time. When the
C++ strategy is set to MANUAL (0), constructors and destructors are not executed
by the loader or unloader. When the C++ strategy is MANUAL, the functions
cplusCtors( ) and cplusDtors( ) can be used to execute the constructors or
destructors for a particular code module.
The function cplusXtorSet( ) is available to change the C++ strategy at runtime.
The default setting is AUTOMATIC.
For more information, see the cplusLib and loadLib entries in the VxWorks API
Reference.
255
VxWorks 5.5
Programmers Guide
The VxWorks loader creates three segments: text, data, and bss. When gathering
sections together to form segments, the sections are placed into the segments in the
same order in which they occur in the ELF file. It is sometimes necessary to add
extra space between sections to satisfy the alignment requirements of all of the
sections. When allocating space for one or more segments, care must be taken to
ensure that there is enough space to permit all of the sections to be aligned
properly. (The alignment requirement of a section is given as part of the section
description in the ELF format. The binary utilities readelfarch and objdumparch
can be used to obtain the alignment information.)
In addition, the amount of padding required between sections depends on the
alignment of the base address. To ensure that there will be enough space without
knowing the base address in advance, allocate the block of memory so that it is
aligned to the maximum alignment requirement of any section in the segment. So,
for instance, if the data segment contains sections requiring 128 and 264 byte
alignment, in that order, allocate memory aligned on 264 bytes.
The other object module formats used in VxWorks (a.out and PECOFF) have basic
units of information that correspond more closely to the VxWorks model of a
single unit each of text, data and bss. Therefore when working with architectures
that use either the a.out or PECOFF file formats, this problem of allocating extra
space between sections does not arise.
The unloader can remove the sections wherever they have been installed, so no
special instructions are required to unload modules that were initially loaded at
specific addresses. However, if the base address was specified in the call to the
loader, then, as part of the unload, unloader does not free the memory area used to
hold the segment. This allocation was performed by the caller, and the
de-allocation must be as well.
A relocatable file is an object file for which text and data sections are in a transitory
form, meaning that some addresses are not yet known. An executable file is one
which is fully linked and ready to run at a specified address. In many operating
systems, relocatable object modules are an intermediate step between source (.c, .s,
.cpp) files and executable files, and only executable files may be loaded and run.
256
6
Target Tools
The relocatable files produced by the toolchains are labeled with a .o extension.
However, in VxWorks, relocatable (.o) files are used for application code, for the
following reason. To construct an executable image for download, the programs
execution address and the addresses of externally defined symbols, such as library
routines, must be known. Since the layout of the VxWorks image and downloaded
code in memory are so flexible, these items of information are not available to a
compiler running on a host machine. Therefore, the code handled by the
target-resident loader must be in relocatable form, rather than an executable.
Once installed in the system's memory, the entity composed of the object module's
code, data, and symbols is called a code module. For information about installed
code modules, see the individual routine entries under moduleLib in the VxWorks
API Reference.
There are several standard formats for both relocatable and executable object files.
In the Tornado development environment, there is a single preferred object
module format for each supported target architecture. For most architectures, the
preferred format is now Executable and Linkable Format (ELF). Exceptions are the
68K toolchain, which produces a.out object files, and the NT simulator toolchain,
which produces pecoff object files.
The VxWorks loader can handle most object files generated by the supported
toolchains.
NOTE: Not all possible relocation operations that are supported by an architecture
are necessarily supported by the loader. This will usually only affect users writing
assembly code.
ELF object module format
257
VxWorks 5.5
Programmers Guide
most one segment of each type.) The loader installs the following categories of
sections in the system's memory:
Read-only data sections are placed in the text segment by the loader.
a.out Object Module Format
Object files in the a.out file format are fairly simple, and are already very close to
the VxWorks abstraction of code modules. Similarly to ELF files, they contain both
headers and sections. In other literature on file formats, the 'sections' of an a.out file
are sometimes referred to as sections and sometimes as segments, even within the
same work, depending on the context. Since only one text section, one data section,
and one bss section are permitted in an a.out file, a.out sections are essentially
equivalent segments, for our purposes.
The text, data, and bss sections of the a.out object module become the text, data
and bss segments of the loaded code module.
PECOFF Object Module Format
The PECOFF object module format is only used in VxWorks for the Windows
simulator architecture.
The basic unit of information in a PECOFF object module is also called a section.
The allowed size a section is limited by the PECOFF file format. Therefore, it is
possible, although not common, to have more than one text or data section in a
PECOFF file.
The VxWorks target loader only handles PECOFF object files that have at most one
text section, one data section, and one bss section. These sections become the text,
data, and bss segments of VxWorks code module.
The VxWorks loader performs some of the same tasks as a traditional linker in that
it prepares the code and data of an object module for the execution environment.
This includes the linkage of the module's code and data to other code and data.
258
6
Target Tools
The loader is unlike a traditional linker in that it does this work directly in the
target system's memory, and not in producing an output file.
In addition, the loader uses routines and variables that already exist in the
VxWorks system, rather than library files, to relocate the object module that it
loads. The system symbol table (see 6.4.4 Using the VxWorks System Symbol Table,
p.266) is used to store the names and addresses of functions and variables already
installed in the system.This has the side effect that once symbols are installed in the
system symbol table, they are available for future linking by any module that is
loaded. Moreover, when attempting to resolve undefined symbols in a module, the
loader uses all global symbols compiled into the target image, as well as all global
symbols of previously loaded modules. As part of the normal load process, all of
the global symbols provided by a module are registered in the system symbol
table. You can override this behavior by using the LOAD_NO_SYMBOLS load flag
(see Table 6-4).
The system symbol table allows name clashes to occur. For example, suppose a
symbol named func exists in the system. A second symbol named func is added to
the system symbol table as part of a load. From this point on, all links to func are
to the most recently loaded symbol. See also, 6.4.1 Configuring VxWorks with Symbol
Tables, p.262.
The VxWorks loader loads code modules in a sequential manner. That is, a separate
load is required for each separate code module. Suppose a user has two code
modules named A_module and B_module, and A_module references symbols
that are contained in B_module. The user may either use the host-resident linker
to combine A_module and B_module into a single module, or may load
B_module first, and then load A_module.
When code modules are loaded, they are irreversibly linked to the existing
environment; meaning that, once a link from a module to an external symbol is
created, that link cannot be changed without unloading and reloading the module.
Therefore dependencies between modules must be taken into account when
modules are loaded to ensure that references can be resolved for each new module,
using either code compiled into the VxWorks image or modules that have already
been loaded into the system.
Failure to do so results in incompletely resolved code, which retains references to
undefined symbols at the end of the load process. For diagnostic purposes, the
loader prints a list of missing symbols to the console. This code should not be
259
VxWorks 5.5
Programmers Guide
Common symbols provide a challenge for the VxWorks loader that is not
confronted by a traditional linker. Consider the following example:
#include <stdio.h>
int willBeCommon;
void main (void) {}
{
...
}
260
6
Target Tools
controlling how common symbols are handled and different default behavior. For
details, see the reference entry for usrLib.
Symbol Entries
261
VxWorks 5.5
Programmers Guide
type
The type is provides additional information about the symbol. For symbols in
the system symbol table, it is one of the types defined in
installDir/target/h/symbol.h; for example, SYM_UNDF, SYM_TEXT, and so on.
For user symbol tables, this field can be user-defined.
Symbol Updates
The symbol table is updated whenever modules are loaded into, or unloaded from,
the target. You can control the precise information stored in the symbol table by
using the loader options listed in Table 6-4.
You can easily search all symbol tables for specific symbols. To search from the
shell, use lkup( ). For details, see the lkup( ) reference entry. To search
programmatically, use the symbol library API's, which can be used to search the
symbol table by address, by name, and by type, and a function that may be used
to apply a user-supplied function to every symbol in the symbol table. For details,
see the symLib reference entry.
Basic Configuration
The most basic configuration for a symbol table is to include the component,
INCLUDE_SYM_TBL. This provides the basic symbol table library, symLib, (which
is not equivalent to the system symbol table) and sufficient configuration for
262
6
Target Tools
creating user symbol tables. The symbol table library component includes
configuration options, which allow you to modify the symbol table width and
control whether name clashes are permitted.
263
VxWorks 5.5
Programmers Guide
When the system symbol table is first created at system initialization time, it
contains no symbols. Symbols must be added to the table at run-time. Each of these
components handles the process of adding symbols differently.
NOTE: When building in a BSP directory, rather than using the project facility,
including the component INCLUDE_STANDALONE_SYM_TBL is not sufficient.
A built-in system symbol table relies on the makeSymTbl utility to obtain the
symbol information. This utility uses the gnu utility nmarch to generate
information about the symbols contained in the image. Then it processes this
information into the file symTbl.c that contains an array, standTbl, of type
SYMBOL described in Symbol Entries, p.261. Each entry in the array has the symbol
name and type fields set. The address (value) field is not filled in by makeSymTbl.
The symTbl.c file is treated as a normal .c file, and is compiled and linked with the
rest of the VxWorks image. As part of the normal linking process, the toolchain
linker fills in the correct address for each global symbol in the array. When the
build completes, the symbol information is available in the image as a global array
of VxWorks SYMBOL's. After the kernel image is loaded into target memory at
system initialization, the information from the global SYMBOL array is used to
construct the system symbol table.
The definition of the standTbl array can be found in the following files (only after
a build). These files are generated as part of the build of the VxWorks image:
264
6
Target Tools
installDir/target/config/BSPname/symTbl.c
for images build directly from a BSP directory
installDir/target/proj/projDir/buildDir/symTbl.c
for images using the project facility
Although this method creates a resulting VxWorks image (module file) that is
larger than it would be without symbols, the built-in symbol table has advantages,
some of which are useful when the download link is slow. These advantages are:
It saves memory if you are not otherwise using the target loader, because it
does not require the target loader (and associated components) to be
configured into the system.
It does not require the target to have access to a host (unlike the downloadable
system symbol table).
It is faster than loading two files (the image and .sym files) because network
operations4 on a file take longer than the data transfer to memory.
The loadable system symbol table uses a vxWorks.sym file, rather than the
symTbl.c file. The vxWorks.sym file is created by using the objcopy utility to strip
all sections, except the symbol information, from the final VxWorks image. The
resulting symbol information is placed into a file named vxWorks.sym.
For architectures using the ELF object module format for the VxWorks image, the
vxWorks.sym file is also in the ELF format, and contains only a SYMTAB section
4. That use open( ), seek( ), read( ), and close( ).
265
VxWorks 5.5
Programmers Guide
and a STRTAB section. In general, the file vxWorks.sym is in the same object format
as other object files compiled for the target by the installed toolchain.
During boot and initialization, the vxWorks.sym file is downloaded using the
loader, which directly calls loadModuleAt( ). To download the vxWorks.sym file,
the loader uses the current default device, which is described in 4.2.1 Filenames and
the Default Device, p.109.
To download the VxWorks image, the loader also uses the default device, as is
current at the time of that download. Therefore, the default device used to
download the vxWorks.sym file may, or may not, be the same device. This is
because the default device can be set, or reset, by other initialization code that runs.
This modification can happen after the VxWorks image is downloaded, but before
the symbol table is downloaded.
Nevertheless, in standard VxWorks configurations, that do not include customized
system initialization code, the default device at the time of the download of the
vxWorks.sym, is usually set to one of the network devices, and using either rsh or
ftp as the protocol.
266
6
Target Tools
name and address information for all of the code statically compiled into the
system or dynamically downloaded. (You can use the LOAD_NO_SYMBOLS
option to hide loaded modules, so that their symbols do not appear in the system
symbol table; see Table 6-5).
If the facilities provided by the symbol table library are needed for non-operating
system code, another symbol table may be created and manipulated using the
symbol library.
Symbols are dynamically added to, and removed from, the system symbol table
each time any of the following occurs:
by the wdb agent when synchronizing symbol information with the host
The exact dependencies between the system symbol table and the other target tools
are as follows:
Loader. The loader requires the system symbol table. The system symbol table
does not require the presence of the loader. The target-based loader and code
module management requires the system symbol table when loading code that
must be linked against code already on the target.
Debugging Tools. The target-based symbolic debugging, facilities and user
commands such as i and tt, rely on the system symbol table to provide information
about entry points of tasks, symbolic contents of call stacks, and so on. Without the
system symbol table, they can still be used but, their usefulness is greatly reduced.
Target Shell. The target shell does not strictly require the system symbol table, but
its functionality is greatly limited without it. The target shell requires the system
symbol table to provide the ability to run functions using their symbolic names.
The target-based shell uses the system symbol table to execute shell commands, to
call system routines, and to edit global variables. The target shell also includes the
library usrLib, which contains the commands i, ti, sp, period, and bootChange.
wdb Agent. The wdb agent adds symbols to the system symbol table as part of the
267
VxWorks 5.5
Programmers Guide
NOTE: If you choose to use both the host-resident and target-resident tools at the
same time, use the synchronization method to ensure that both the host and target
resident tools share the same list of symbols. The synchronization applies only to
symbols and modules, not to other information such as breakpoints.
268
6
Target Tools
system service at the time of the call and may not reflect the current state of the
system. To use these routines, you must define the associated configuration macro
(see the Tornado Users Guide: Projects). When you invoke them, their output is sent
to the standard output device. Table 6-8 lists common system show routines:
Table 6-8
Show Routines
Call
Description
Configuration Macro
envShow( )
INCLUDE_TASK_SHOW
memPartShow( )
memShow( )
moduleShow( )
msgQShow( )
INCLUDE_POSIX_MQ_SHOW
INCLUDE_MSG_Q_SHOW
semShow( )
INCLUDE_SEM_SHOW,
INCLUDE_POSIX_SEM_SHOW
show( )
stdioShow( )
INCLUDE_STDIO_SHOW
taskSwitchHookShow( )
INCLUDE_TASK_HOOKS_SHOW
taskCreateHookShow( )
INCLUDE_TASK_HOOKS_SHOW
taskDeleteHookShow( )
INCLUDE_TASK_HOOKS_SHOW
taskShow( )
INCLUDE_TASK_SHOW
wdShow( )
INCLUDE_WATCHDOGS_SHOW
INCLUDE_MEM_SHOW
269
VxWorks 5.5
Programmers Guide
Table 6-9
Description
ifShow( )
inetstatShow( )
ipstatShow( )
Display IP statistics.
netPoolShow( )
netStackDataPoolShow( )
netStackSysPoolShow( )
mbufShow( )
netShowInit( )
arpShow( )
arptabShow( )
routestatShow( )
routeShow( )
hostShow( )
mRouteShow( )
I set a breakpoint on a function I called from the target shell, but the breakpoint is
not being hit. Why not?
270
6
Target Tools
Solution
Instead of running the function directly, use taskSpawn( ) with the function as the
entry point.
Explanation
The target shell task runs with the VX_UNBREAKABLE option. Functions that are
called directly from the target shell command prompt, are executed within the
context of the target shell task. Therefore, breakpoints set within the directly called
function will not be hit.
Insufficient Memory
Solution
Download the file using a different device. Loading an object module from a host
file system mounted through NFS only requires enough memory for one copy of
the file (plus a small amount of overhead).
Explanation
The target-resident loader calls the device drivers through a VxWorks transparent
mechanism for file management, which makes calls to open, close, and ioctl. If you
use the target-resident loader to load a module over the network (as opposed to
loading from a target-system disk), the amount of memory required to load an
object module depends on what kind of access is available to the remote file system
over the network. This is because, depending on the device that is actually being
used for the load, the calls initiate very different operations.
For some devices, the I/O library makes a copy of the file in target memory.
Loading a file that is mounted over a device using such a driver requires enough
memory to hold two copies of the file simultaneously. First, the entire file is copied
to a buffer in local memory when opened. Second, the file resides in memory when
it is linked to VxWorks. This copy is then used to carry out various seek and read
operations. Therefore, using these drivers requires sufficient memory available to
hold two copies of the file to be downloaded, as well as a small amount of memory
for the overhead required or the load operation.
271
VxWorks 5.5
Programmers Guide
The actual number received in the error message varies (26, or 23, or ...) depending
on the architecture. What does this error mean and what should I do?
Solution
Recompile the object file using -Xcode-absolute-far for the Diab compilers, and for
GNU compilers, the appropriate long call option, -mlongCallOption.
Explanation
Some architectures have instructions that use less than 32 bits to reference a nearby
position in memory. Using these instructions can be more efficient than always
using 32 bits to refer to nearby places in memory.
The problem arises when the compiler has produced such a reference to something
that lies farther away in memory than the range that can be accessed with the
reduced number of bits. For instance, if a call to printf is encoded with one of these
instructions, the load may succeed if the object code is loaded near the kernel code,
but fail if the object code is loaded farther away from the kernel image.
Missing Symbols
Symbols in code modules downloaded from the host do not appear from the target
shell, and vice versa. Symbols created from the host shell are not visible from the
target shell, or symbols created from the target shell are not visible from the host
shell. Why is this happening, and how can I get them to appear?
Solution
Check to see if the symbol synchronization is enabled for the target server as well
as compiled into the image. For more information, see 6.4.5 Synchronizing Host and
Target-Resident Symbol Tables, p.268.
Explanation
272
6
Target Tools
Including the target loader causes the amount of available memory to be much
smaller. How can I get more memory?
Solution
Use the host tools (windsh, host loader, and so on) rather than the target tools and
remove all target tools from your VxWorks image. For details, see the Tornado
Users Guide.
Explanation
Including the target loader causes the system symbol table to be included. This
symbol table contains the name, address, and type of every global symbol in the
compiled VxWorks image.
Using the target-resident loader takes additional memory away from your
applicationmost significantly for the target-resident symbol table required by
the target-resident loader.
The system symbol table failed to download onto my target. How can I use the
target shell to debug the problem, since I cannot call functions by name?
Solution
Use addresses of functions and data, rather than using the symbolic names. The
addresses can be obtained from the VxWorks image on the host, using the nmarch
utility.
The following is an example from a Unix host:
> nmarch vxWorks | grep memShow
0018b1e8 T memShow
0018b1ac T memShowInit
273
VxWorks 5.5
Programmers Guide
Use this information to call the function by address from the target shell. (The
parentheses are mandatory when calling by address.)
-> 0x0018b1e8 ()
status
bytes
blocks
avg block max block
------ --------- -------- ---------- ---------current
free
14973336
20
748666
12658120
alloc 14201864
16163
878
cumulative
alloc 21197888
value = 0 = 0x0
274
142523
148
7
C++ Development
7.1 Introduction
This chapter provides information about C++ development for VxWorks using the
Wind River GNU and Diab toolchains.
!
WARNING: GNU C++ and Diab C++ binary files are not compatible.
For documentation on using C++ with the Tornado tools, see the chapters (either
in the this manual or in the Tornado Users Guide) that are specific to the tool.
WARNING: Any VxWorks task that uses C++ must be spawned with the
VX_FP_TASK option. Failure to use the VX_FP_TASK option can result in hard-to-
275
VxWorks 5.5
Programmers Guide
You can also use this syntax to make C symbols accessible to C++ code. VxWorks
C symbols are automatically available to C++ because the VxWorks header files
use this mechanism for declarations.
Includes support for C++ language features such as new, delete, and exception
handling.
276
7
C++ Development
Includes all basic C++ run-time support in VxWorks. This enables you to
download and run compiled and munched C++ modules.
INCLUDE_CPLUS_STL
Includes the full iostream library; this component requires and automatically
includes INCLUDE_CPLUS_IOSTREAMS.
INCLUDE_CPLUS_STRING_IO
Includes I/O for complex number objects; this component requires and
automatically includes INCLUDE_CPLUS_IOSTREAMS and
INCLUDE_CPLUS_COMPLEX.
Diab-Specific Library Support Components
For the Diab compiler, all C++ library functionality is encapsulated in a single
component:
INCLUDE_CPLUS_IOSTREAMS
277
VxWorks 5.5
Programmers Guide
Ensures that the C++ run-time support calls the correct constructors and
destructors in the correct order for all static objects.
278
7
C++ Development
For each toolchain, the following examples compile a C++ application source file,
hello.cpp, run munch on the .o, compile the generated ctdt.c file, and link the
application with ctdt.o to generate a downloadable module, hello.out.
Using GNU
The following code includes comments and the commands to perform these steps
on hello.cpp using the GNU toolchain:
# Compile
ccppc -mcpu=604 -mstrict-align -O2 -fno-builtin -IinstallDir/target/h \
-DCPU=PPC604 -DTOOL_FAMILY=gnu -DTOOL=gnu -c hello.cpp
# Run munch
nmppc hello.o | wtxtcl installDir/host/src/hutils/munch.tcl \
-c ppc > ctdt.c
# Compile ctdt.c file generated by munch
ccppc -mcpu=604 -mstrict-align -fdollars-in-identifiers -O2 \
-fno-builtin -IinstallDir/target/h \
-DCPU=PPC604 -DTOOL_FAMILY=gnu -DTOOL=gnu -c ctdt.c
# Link hello.o with ctdt.o to give a downloadable module (hello.out)
ccppc -r -nostdlib -Wl,-X -T installDir/target/h/tool/gnu/ldscripts/link.OUT \
-o hello.out hello.o ctdt.o
NOTE: The -T .../link.OUT option collapses any linkonce sections contained in the
input files (for details, see -fmerge-templates, p.282). It should not be used on 68k,
VxSim Solaris, or VxSim PC. Instead, you can use, for example:
cc68k -r -nostdlib -Wl,-X -o hello.out hello.o ctdt.o
Using Diab
The following code includes comments and the commands to perform these steps
on hello.cpp using the Diab toolchain:
# Compile
dcc -tMCF5307FS:vxworks55 -W:c:,-Xmismatch-warning=2 \
-ew1554,1551,1552,1086,1047,1547 -Xclib-optim-off -Xansi \
-Xstrings-in-text=0 -Wa,-Xsemi-is-newline -ei1516,1643,1604 \
-Xlocal-data-area-static-only -ew1554 -XO -Xsize-opt -IinstallDir/target/h \
-DCPU=MCF5200 -DTOOL_FAMILY=diab -DTOOL=diab -c hello.cpp
# Run munch
nmcf hello.o | wtxtcl <italic>installDir</italic>/host/src/hutils/munch.tcl \
-c cf > ctdt.c
279
VxWorks 5.5
Programmers Guide
If you use the VxWorks Makefile definitions, you can write a simple munching rule
which (with appropriate definitions of CPU and TOOL) works across all
architectures for both GNU and Diab toolchains.
CPU
TOOL
= PPC604
= gnu
TGT_DIR = $(WIND_BASE)/target
include $(TGT_DIR)/h/make/defs.bsp
default : hello.out
%.o : %.cpp
$(CXX) $(C++FLAGS) -c $<
%.out : %.o
$(NM) $*.o | $(MUNCH) > ctdt.c
$(CC) $(CFLAGS) $(OPTION_DOLLAR_SYMBOLS) -c ctdt.c
$(LD_PARTIAL) $(LD_PARTIAL_LAST_FLAGS) -o $@ $*.o ctdt.o
After munching, downloading, and linking, the static constructors and destructors
are called. This step is described next.
280
7
C++ Development
manual invocation
Requires the user to call static constructors manually, after downloading the
module, but before running the application. Requires the user to call static
destructors manually, after the task finishes running, but before unloading the
module. Static constructors are called by invoking cplusCtors( ). Static
destructors are called by invoking cplusDtors( ). These routines take a module
as an argument; thus, static constructors and destructors are called explicitly
on a module-by-module basis. However, you can also invoke all currentlyloaded static constructors or destructors by calling these routines with no
argument.
!
CAUTION: When using the manual invocation method, constructors for each
281
VxWorks 5.5
Programmers Guide
-fimplicit-templates
This is the option for implicit instantiation. Using this strategy, the code for each
template gets emitted in every module that needs it. For this to work the body of a
template must be available in each module that uses it. Typically this is done by
including template function bodies along with their declarations in a header file.
The advantage of implicit instantiation is that it is simple and it is used by default
in VxWorks makefiles. The disadvantage is that it may lead to code duplication
and larger application size.
-fmerge-templates
ignored by the 68k and simulator compilers (cc68k, ccsimso, and ccsimpc).
!
CAUTION: The VxWorks dynamic loader does not support linkonce sections
directly. Instead, the linkonce sections must be merged and collapsed into standard
text and data sections before loading. This is done with a special link step
described in 7.3.1 Munching C++ Application Modules, p.278.
-fno-implicit-templates
This is the option for explicit instantiation. Using this strategy explicitly
instantiates any templates that you require. The advantage of explicit instantiation
is that it allows you the most control over where templates get instantiated and
avoids code bloat. This disadvantage is that you need to explicitly instantiate each
template.
-frepo
This is the option for the third approach. This strategy works by manipulating a
database of template instances for each module. The compiler generates a .rpo file,
282
7
C++ Development
for each corresponding object file, which lists all template instantiations that are
used, and could be instantiated, in that object file. The link wrapper, collect2, then
updates the .rpo files to tell the compiler where to place those instantiations, and
rebuilds any affected object files. The link-time overhead is negligible after the first
pass, as the compiler continues to place the instantiations in the same files. The
advantage of this approach is that it combines the simplicity of implicit
instantiation with the smaller footprint obtained by instantiating templates by
hand.
Procedure
The header file for a template must contain the template body. If template bodies
are currently stored in .cpp files, the line #include theTemplate.cpp must be added
to theTemplate.h.
A complete build with the -frepo option is required to create the .rpo files that tell
the compiler which templates to instantiate. The link step should be invoked using
ccarch rather than ldarch.
Subsequently, individual modules can be compiled as usual (but with the -frepo
option and no other template flags).
When a new template instance is required, the relevant part of the project must be
rebuilt to update the .rpo files.
Loading Order
The Tornado tools dynamic linking requires the module containing a symbol
definition to be downloaded before a module that references it. When using the frepo option, it may not be clear which module contains the definition. Thus, you
should also prelink them and download the linked object.
Example
This example uses a standard VxWorks BSP makefile; for concreteness, it uses a
68K target.
Example 7-1
Sample Makefile
make PairA.o PairB.o ADDED_C++FLAGS=-frepo
/* dummy link step to instantiate templates */
cc68k -r -o Pair PairA.o PairB.o
//Pair.h
template <class T> class Pair
283
VxWorks 5.5
Programmers Guide
{
public:
Pair (T _x, T _y);
T Sum ();
protected:
T x, y;
};
template <class T>
Pair<T>::Pair (T _x, T _y) : x (_x), y(_y)
{
}
template <class T>
T Pair<T>::Sum ()
{
return x + y;
}
// PairA.cpp
#include "Pair.h"
int Add (int x, int y)
{
Pair <int> Two (x, y);
return Two.Sum ();
}
// PairB.cpp
#include "Pair.h"
int Double (int x)
{
Pair <int> Two (x, x);
return Two.Sum ();
}
284
7
C++ Development
You can still write code according to the pre-exception model of C++ compilation.
For example, your calls to new can check the returned pointer for a failure value
of zero. However, if you are concerned that the exception handling enhancements
in this release will not compile your code correctly, follow these simple rules:
Do not use string objects or wrap them in try { } catch (...) { } blocks.
GNU iostreams does not throw unless IO_THROW is defined when the library
is built, and exceptions are explicitly enabled for the particular iostreams
object in use. The default is no exceptions. Exceptions have to be explicitly
turned on for each iostate flag that wants to throw.
The Standard Template library (STL) does not throw except in some methods
in the basic_string class (of which string is a specialization).
For each non-inlined function that uses exception handling, 29 instructions are
executed to perform the exception-handling setup.
285
VxWorks 5.5
Programmers Guide
first time
normal case
// 3+29
3+29
// 1235
1235
void doit() {
try {
test();
} catch (...) {
printf("Hi\n");
}
}
// 3+29+947
// 22
// 1
3+29
22
1
//
//
//
3+29
14
4
struct A { ~A( ) { } };
void local_var ( ) {
A a;
}
You can turn off exception handling by using the -fno-exceptions option. Doing so
removes all exception handling overhead.
Unhandled Exceptions
7.4.4 Namespaces
The GNU C++ compiler supports namespaces. You can use namespaces for your
own code, according to the C++ standard.
286
7
C++ Development
The final version of the C++ standard also defines names from system header files
in a namespace called std. The standard requires that you specify which names
in a standard header file you will be using. For the std namespace, the GNU C++
compiler accepts the new format, but does not require it. This is because GNU C++
puts the std namespace into the global namespace.
This means that the current GNU C++ compiler is transitional, compiling both
legacy code and new code written according to the standard. However, remember
when coding under the new syntax that identifiers in the std namespace will be
global; and therefore, they must be unique for the global namespace.
As an example, the following code is technically invalid under the latest standard,
but will compile under the current GNU C++ compiler:
#include <iostream.h>
int main()
{
cout << "Hello, world!" << endl;
}
The following three examples show how the C++ standard would now represent
this code. These examples will also compile under the current GNU C++ compiler:
// Example 1
#include <iostream>
int main()
{
std::cout << "Hello, world!" << std::endl;
}
// Example 2
#include <iostream>
using std::cout;
using std::endl;
int main()
{
cout << "Hello, world!" << endl;
}
// Example 3
#include <iostream>
using namespace std;
int main()
{
cout << "Hello, world!" << endl;
}
Note that the using directives is accepted, but not required by GNU C++.
287
VxWorks 5.5
Programmers Guide
The same applies to standard C code. For example, both of these code examples
will compile:
#include <stdio.h>
void main()
{
int i = 10;
printf("%d", &i");
}
or
#include <cstdio>
void main()
{
int i = 10;
std::printf("%d", &i");
}
288
7
C++ Development
There are two ways to control instantiation of templates. By default, templates are
instantiated implicitly--that is, they are instantiated by the compiler whenever a
template is used. For greater control of template instantiation, the -Ximplicittemplates-off option tells the compiler to instantiate templates only where
explicitly called for in source code; for example:
template class A<int>;
template int f1(int);
-Ximplicit-templates
-Ximplicit-templates-off
-Xcomdat
When templates are instantiated implicitly, mark each generated code or data
section as comdat. The linker collapses identical instances marked as such, into a
single instance in memory.
VxWorks cannot load object files that contain comdats, however, you can use
-Xcomdat-on with the Diab linker, and load the resulting executable.
-Xcomdat-off
289
VxWorks 5.5
Programmers Guide
These classes are part of the new Standard C++ library. For the Diab toolchain these
classes are provided entirely in header files. For the GNU toolchain they can be
configured into a VxWorks system with INCLUDE_CPLUS_STRING and
INCLUDE_CPLUS_COMPLEX. You may optionally include I/O facilities for these
classes with INCLUDE_CPLUS_STRING_IO and INCLUDE_CPLUS_COMPLEX_IO.
iostreams Library
290
7
C++ Development
For the GNU toolchain, the iostreams library header files reside in
installDir/host/hostType/include/g++-3 directory. For the Diab toolchain, the
iostreams library header files reside in the installDir/host/diab/include/cpp
directory.
!
To use this library, include one or more of the header files after the vxWorks.h
header in the appropriate modules of your application. The most frequently used
header file is iostream.h, but others are available; see a C++ reference such as
Stroustrup for information.
The standard iostreams objects (cin, cout, cerr, and clog) are global; that is, they are
not private to any given task. They are correctly initialized regardless of the
number of tasks or modules that reference them and they can safely be used across
multiple tasks that have the same definitions of stdin, stdout, and stderr. However
they cannot safely be used when different tasks have different standard I/O file
descriptors; in such cases, the responsibility for mutual exclusion rests with the
application.
The effect of private standard iostreams objects can be simulated by creating a new
iostreams object of the same class as the standard iostreams object (for example, cin
is an istream_withassign), and assigning to it a new filebuf object tied to the
appropriate file descriptor. The new filebuf and iostreams objects are private to the
calling task, ensuring that no other task can accidentally corrupt them.
ostream my_out (new filebuf (1));
/* 1 == STDOUT */
istream my_in (new filebuf (0), &my_out); /* 0 == STDIN;
* TIE to my_out */
For complete details on the iostreams library, see the GNU ToolKit Users Guide.
For both toolchains, the Standard Template library consists entirely of a set of
header files. There is no special run-time component that must be included in a
VxWorks system.
291
VxWorks 5.5
Programmers Guide
The GNU STL port for VxWorks is thread safe at the class level. This means that
the client has to provide explicit locking, if two tasks want to use the same
container object (a semaphore can be used to do so; see 2.3.3 Semaphores, p.34).
However, two different objects of the same STL container class can be accessed
concurrently.
The C++ Standard Template Library can be used in client code compiled with
exception handling turned off. In our implementation this has the following
semantics:
(1) For all checks that could reasonably be made by the caller, such as bounds
checking, no action is taken where an exception would have been thrown.
With optimization on, this is equivalent to removing the check.
(2) For memory exhaustion where bad_alloc would have been thrown, now the
following message is logged (if logging is included):
"STL Memory allocation failed and exceptions disabled -calling terminate"
and the task calls terminate. This behavior applies only to the default
allocators; you are free to define custom allocators with a different behavior.
STL for Diab
The Diab C++ library is fully compliant with the ANSI C++ Standard with the
following minor exception: it does not have full wchar support. The Diab STL
component is thread safe.
292
7
C++ Development
For the factory demo, your kernel must include the following components:
INCLUDE_CPLUS
INCLUDE_CPLUS_LANG
INCLUDE_CPLUS_IOSTREAMS
INCLUDE_CPLUS_STRING
INCLUDE_CPLUS_STRING_IO
To add components from the Tornado IDE, see the Tornado Users Guide: Projects.
To build factory from the command line, simply copy the factory sources to the
BSP directory, as shown below:
cd installDir/target/config/bspDir
cp installDir/target/src/demo/cplusplus/factory/factory.* .
Then, to build a bootable image containing the factory example, run make as
shown below:
make ADDED_MODULES=factory.o
Then, from the WindSh, load the factory module, as shown below:
ld < factory.out
Full documentation on what you should expect to see is provided in the source
code comments for the demo program.
293
VxWorks 5.5
Programmers Guide
294
8
Flash Memory Block
Device Driver
Optional Component TrueFFS
8.1 Introduction
TrueFFS for Tornado is an optional product that provides a block device interface
to a wide variety of flash memory devices. TrueFFS is a VxWorks-compatible
implementation of M-Systems FLite, version 2.0. This system is reentrant,
thread-safe, and supported on all CPU architectures that host VxWorks.
This chapter begins with a brief introduction to flash memory, followed by a
step-by-step outline of the procedure for building a system with TrueFFS. Then,
the main part of the chapter describes these steps in detail, including sections
devoted to writing your own socket driver and MTD components. The chapter
ends with a description of the functionality of flash memory block devices.
NOTE: This version of the TrueFFS product is a block device driver to VxWorks
that, although intended to be file system neutral, is guaranteed to work only with
the MS-DOS compatible file system offered with this product.
295
VxWorks 5.5
Programmers Guide
Flash memory stores data indefinitely, even while unpowered. The physical
components of flash memory are solid-state devices1 that consume little energy
and leave a small foot print. Thus, flash memory is ideal for mobile devices, for
hand-held devices, and for embedded systems that require reliable, non-volatile
environments that are too harsh for mechanical disks.
However, flash does have a limited life, due to the finite number of erase cycles;
and, TrueFFS only supports flash devices that are symmetrically blocked. In
addition, some features common to block device drivers for magnetic media are
not available with flash memory. Unequal read and write time is a typical
characteristic of flash memory, in which reads are always faster than writes. For
more information, see 8.13 Flash Memory Functionality, p.338. Also, TrueFFS does
not support ioctl.
The unique qualities that distinguish flash memory from magnetic-media disk
drives make it an ideal choice for some types of applications, yet impractical for
others.
NOTE: Although you can write in any size chunks of memory, ranging from bytes
to words and double words, you can only erase in blocks. The best approach to
extending the life of the flash is to ensure that all blocks wear evenly. For more
information, see 8.13 Flash Memory Functionality, p.338.
NOTE: The TrueFFS optional product does have not support for partition tables.
CAUTION: VxWorks favors task priorities over I/O sequence when servicing I/O
requests. Thus, if a low priority task and a high priority task request an I/O service
while the resource is busy, the high priority task gets the resource when it becomes
availableeven if the low priority got its request in before the high priority task.
To VxWorks, a flash device is just another resource.
296
8
Flash Memory Block Device Driver
Figure 8-1
dosFs
Core
Layer
TrueFFS
Translation Layer
MTDs
Socket Layer
Flash Memory
Core Layer This layer connects other layers to each other. It also channels work to
the other layers and handles global issues, such as backgrounding, garbage
collection, timers, and other system resources. The core layer is provided in binary
form only.
Translation Layer This layer maintains the map that associates the file systems
view of the storage medium with the erase blocks in flash. The Block Allocation
Map is the basic building block for implementing wear leveling and error recovery.
The translation layer is media specific (NOR or SSFDC). The translation layer is
provided in binary form only.
MTD Layer The MTD implements the low-level programming (map, read, write,
and erase) of flash medium. MTDs are provided in both source and binary form.
Socket Layer The socket layer provides the interface between TrueFFS and the
297
VxWorks 5.5
Programmers Guide
Choose an MTD, appropriate to your hardware, from those provided with the
TrueFFS product. You may prefer to (or, in rare cases, need to) write your own. For
details, see 8.3 Selecting an MTD Component, p.299.
Step 2:
Ensure that you have a working socket driver. The socket driver is a source code
component, implemented in the file sysTffs.c. For some BSPs, the socket driver is
fully defined and located in the BSP directory. If it is not, you can port a generic file
containing skeleton code to your hardware. For details, see 8.4 Identifying the Socket
Driver, p.300.
Step 3:
as might be expected.
Step 4:
Before you build the system, the binaries for the MTDs need to be up to date and
the socket driver file located in the BSP directory must be a working version. For
details, see 8.5.7 Building the System Project, p.305.
Step 5:
Next, boot the target. Then, from the shell, format the drives. For details, see
8.6 Formatting the Device, p.306.
NOTE: To preserve a region of flash for boot code, see 8.7 Creating a Region for
298
8
Flash Memory Block Device Driver
Step 6:
Mount the VxWorks DOS file system on a TrueFFS flash drive. For details, see
8.8 Mounting the Drive, p.312.
Step 7:
MTDs that work with several of the devices provided by Intel, AMD, Fujitsu,
and Sharp.
Two generic MTDs that can be used for devices complying with CFI.
To better support the out-of-box experience, these MTDs attempt to cover the
widest possible range of devices (in their class) and of bus architectures.
Consequently, the drivers are bulky and slow in comparison to drivers written
specifically to address the runtime environment that you want to target. If the
performance and size of the drivers provided do not match your requirements,
you can modify them to better suit your needs. The section 8.12 Writing MTD
Components, p.327 has been provided exclusively to address this issue.
299
VxWorks 5.5
Programmers Guide
For a complete list of these MTDs, see 8.11 Using the MTD-Supported Flash Devices,
p.323. In addition, section 8.11.1 Supporting the Common Flash Interface (CFI), p.323
describes the CFI MTDs in detail. Evaluate whether any of these drivers support
the device that you intend to use for TrueFFS. Devices are usually identified by
their JEDEC IDs. If you find an MTD appropriate to your flash device, you can use
that MTD. These drivers are also provided in binary form; so you do not need to
compile the MTD source code unless you have modified it.
NOTE: For a list of the MTD components and details about adding the MTD
component to your system project, see 8.5.4 Including the MTD Component, p.303.
at least one software module from each of the three TrueFFS layers
300
8
Flash Memory Block Device Driver
You can configure and build your system either from the command line or by using
the Tornado IDE project facility. When choosing a method for configuring and
building systems with TrueFFS, consider the following criteria:
Configuring and building from the command line involves editing text files
that contain component listings and parameters for initialization, and calling
the make utility to build a system image for you. This process, while possibly
faster than using the project facility, requires that you provide the list of
dependent components accurately.
For both configuration and build methods, special consideration must be given to
cases where either the socket driver or the MTD, or both, are not provided. The
drivers need to be registered and MTDs need appropriate component descriptions.
For more information, see 8.10 Writing Socket Drivers, p.314, and 8.12.4 Defining
Your MTD as a Component, p.335.
For general information on configuration procedures, see the Tornado Users Guide:
Configuration and Build and the Tornado Users Guide: Projects.
NOTE: Included with TrueFFS for Tornado are sources for several MTDs and
socket drivers. The MTDs are in target/src/drv/tffs. The socket drivers are defined
in the sysTffs.c files provided in the target/config/bspname directory for each BSP
that supports TrueFFS.
301
VxWorks 5.5
Programmers Guide
302
8
Flash Memory Block Device Driver
Defining this component triggers the correct sequence of events, at boot time, for
initializing this product. It also ensures that the socket driver is included in your
project (see 8.5.6 Adding the Socket Driver, p.305).
You can include this component using the project facility, as shown in Figure 8-2,
or by defining INCLUDE_TFFS in config.h.
303
VxWorks 5.5
Programmers Guide
The MTD components provided by TrueFFS, for flash devices from Intel, AMD,
Fujitsu, and Sharp, are listed below:
INCLUDE_MTD_CFISCS
CFI-compliant AMD and Fujitsu devices; for details, see AMD/Fujitsu CFI
Flash Support, p.325.
INCLUDE_MTD_I28F016
Intel 28f016; for details, see Intel 28F016 Flash Support, p.325.
INCLUDE_MTD_I28F008
Intel 28f008; for details, see Intel 28F008 Flash Support, p.326.
INCLUDE_MTD_AMD
AMD, Fujitsu: 29F0{40,80,16} 8-bit devices; for details, see AMD/Fujitsu Flash
Support, p.326.
INCLUDE_MTD_WAMD
recommended for the MTD component because it can conflict with the project
facility configurations.
304
8
Flash Memory Block Device Driver
INCLUDE_TL_FTL
The translation layer for NOR flash devices. If you can execute code in flash,
your device uses NOR logic.
INCLUDE_TL_SSFDC
The translation layer for devices that conform to Toshibas Solid State Floppy
Disk Controller Specifications. TrueFFS supports NAND devices that only
comply with the SSFDC specification.
The component descriptor files (for the project facility) specify the dependency
between the translation layers and the MTDs; therefore, when configuring through
the project facility, you do not need to explicitly select a translation layer. The build
process handles it for you.
If you are not using the project facility, you are responsible for selecting the correct
translation layer. As with the MTD, when configuring and building from the
command line, you define the translation layer in sysTffs.c within the conditional
clause #ifndef PROJECT_BUILD. For details, see nConditional Compilation, p.305.
For more information, see 8.12.4 Defining Your MTD as a Component, p.335.
all the MTD components provided with the TrueFFS product. This
PROJECT_BUILD clause conditionally includes all of these constants for a
305
VxWorks 5.5
Programmers Guide
command-line build (adds them to sysTffs.o), and excludes them for a project
facility build (because you include them from the GUI). Therefore, the same
method must be used to both configure the MTD and the translation layer
components, and to build the project.
If you are building from the command line, and want to save on memory space,
you can undefine the constants for any MTDs that your hardware does not
support.
Up-to-Date Components Before you build the project, the binaries for the MTDs
must be up to date, and the sysTffs.c that is present in the BSP directory must be a
working version.
MTDs are provided in source and binary form. When writing your own MTD,
rebuilding the directory is the recommended way to transform the source to
binary. This way the binary is placed in the right location and added to the
appropriate library. If any files in the group installDir/target/src/drv/tffs/*.c are
newer than corresponding files in the library
installDir/target/lib/archFamily/arch/common/libtffs.a, then rebuild them before
building the project.
NOTE: You can format the flash medium even though there is not yet a block
the file system will leave the board without boot code at the end of it. The board
then becomes unusable until some alternative method is provided for reflashing
the lost image. Once you do that, the file system that you created by formatting is
destroyed.
306
8
Flash Memory Block Device Driver
To facilitate calling tffsDevFormat( ) from a target shell, you can simply pass zero
(or a NULL pointer) for the second argument, formatArg. Doing so will use a
macro, which defines default values for the tffsDevFormatParams structure. The
macro, TFFS_STD_FORMAT_PARAMS, defines the default values used in
formatting a flash disk device. This macro, TFFS_STD_FORMAT_PARAMS, is
defined in tffsDrv.h as:
#define TFFS_STD_FORMAT_PARAMS {{0, 99, 1, 0x10000l, NULL, {0,0,0,0},
NULL, 2, 0, NULL}, FTL_FORMAT_IF_NEEDED}
307
VxWorks 5.5
Programmers Guide
The macro passes values for both the first and second members of the
tffsDevFormatParams structure. These are:
formatParams = {0, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL}
formatFlags = FTL_FORMAT_IF_NEEDED
The meaning of these default values, and other possible arguments for the
members of this structure, are described below.
formatParams Member
Value
Meaning
FTL_FORMAT
FTL_FORMAT_IF_NEEDED
NO_FTL_FORMAT
308
8
Flash Memory Block Device Driver
309
VxWorks 5.5
Programmers Guide
Formatting at an Offset
310
8
Flash Memory Block Device Driver
FTL_FORMAT_IF_NEEDED
};
/* we assume that the drive number 0 is SIMM */
status = tffsDevFormat (0, (int)¶ms);
return (status);
}
The driveNo parameter is the same drive number as the one used as input to
the format routine.
The offset parameter is the actual offset from the start of flash at which the
image is written (most often specified as zero).
The filename parameter is a pointer to the boot image (bootApp or boot ROM
image).
311
VxWorks 5.5
Programmers Guide
comments in installDir/target/src/drv/tffs/tffsConfig.c.
The drive parameter specifies the drive number of the TFFS flash drive; valid
values are 0 through the number of socket interfaces in BSP.
The removable parameter specifies whether the media is removable. Use 0 for
non-removable, 1 for removable.
The fileName parameter specifies the mount point, for example, '/tffs0/'.
The following example runs usrTffsConfig( ) to attach a drive to dosFs, and then
runs devs to list all drivers:
% usrTffsConfig 0,0,"/flashDrive0/"
% devs
drv
0
1
1
5
6
2
name
/null
/tyCo/0
/tyCo/1
host:
/vio
/flashDrive0/
Internally, usrTffsConfig( ) calls other routines, passing the parameters you input.
Among these routines is tffsDevCreate( ), which creates a TrueFFS block device on
top of the socket driver. This routine takes, as input, a number (0 through 4,
312
8
Flash Memory Block Device Driver
inclusive) that identifies the socket driver on top of which to construct the TrueFFS
block device. The tffsDevCreate( ) call uses this number as an index into the array
of FLSocket structures. This number is visible later to dosFs as the driver number.
After the TrueFFS block device is created, dcacheDevCreate( ) and then
dosFsDevCreate( ) are called. This routine mounts dosFs onto the device. After
mounting dosFs, you can read and write from flash memory just as you would
from a standard disk drive.
This example formats RFA and PCMCIA flash for two drives.
The first lines of this example format the board-resident flash by calling the helper
routine, sysTffsFormat( ), which preserves the boot image region. This example
does not update the boot image. It then mounts the drive, numbering it as 0 and
passing 0 as the second argument to usrTffsConfig( ). Zero is used because RFA is
non-removable.
The last lines of the example format PCMCIA flash, passing default format values
to tffsDevFormat( ) for formatting the entire drive. Then, it mounts that drive.
Because PCMCIA is removable flash, it passes 1 as the second argument to
usrTffsConfig( ). (See 8.8 Mounting the Drive, p.312 for details on the arguments to
usrTffsConfig( ).)
313
VxWorks 5.5
Programmers Guide
Insert a flash card in the PCMCIA socket. At the target shell prompt, enter the
following commands:
->
->
->
->
sysTffsFormat
usrTffsConfig 0,0,"/RFA/"
tffsDevFormat 1,0
usrTffsConfig 1,1,"/PCMCIA1/"
mv177 with a Board-Resident Flash Array and No Boot Image Region Created
This example formats PCMCIA flash for two drives. Neither format call preserves
a boot image region. Then, it mounts the drives, the first is numbered 0, and the
second is numbered 1. PCMCIA is a removable medium.
Insert a flash card in each PCMCIA socket. At the target shell prompt, enter the
following commands:
->
->
->
->
tffsDevFormat
usrTffsConfig
tffsDevFormat
usrTffsConfig
0,0
0,1,"/PCMCIA1/"
1,0
1,1,"/PCMCIA2/"
314
8
Flash Memory Block Device Driver
The sysTffsInit( ) routine, which is the main routine. This routine calls the
socket registration routine.
In this stub file, all of the required routines are declared. Most of these routines are
defined completely, although some use generic or fictional macro values that you
may need to modify.
The socket register routine in the stub file is written for RFA (Resident Flash Array)
sockets only. There is no stub version of the registration routine for PCMCIA socket
drivers. If you are writing a socket driver for RFA, you can use this stub file and
follow the steps described in 8.10.1 Porting the Socket Driver Stub File, p.315. If you
are writing a PCMCIA socket driver, see the example in
installDir/target/src/drv/tffs/sockets/pc386-sysTffs.c and the general information
in 8.10.2 Understanding Socket Driver Functionality, p.319.
NOTE: Examples of other RFA socket drivers are in
installDir/target/src/drv/tffs/sockets.
315
VxWorks 5.5
Programmers Guide
2.
Add calls to the registration routine for each additional device (beyond one)
that your BSP supports. Therefore, if you have only one device, you do not
need to do anything for this step. For details, see Call the Socket Register
Routines, p.316.
3.
Review the implementation for the two routines marked /* TODO */. You may
or may not need to add code for them. For details, see Implement the Socket
Structure Member Functions, p.316.
CAUTION: Do not edit the original copy of the stub version of sysTffs.c in
#ifdef INCLUDE_SOCKET_PCIC1
(void) pcRegister (1, PC_BASE_ADRS_1);
#endif /* INCLUDE_SOCKET_PCIC1 */
Define the constants in the BSPs sysTffs.c. Then, you can use them to selectively
control which calls are included in sysTffsInit( ) at compile time.
The stub socket driver file also contains the implementation for the rfaRegister( )
routine, which assigns routines to the member functions of the FLSocket structure,
vol. TrueFFS uses this structure to store the data and function pointers that handle
316
8
Flash Memory Block Device Driver
the hardware (socket) interface to the flash device. For the most part, you need not
be concerned with the FLSocket structure, only with the routines assigned to it.
Once these routines are implemented, you never call them directly; they are called
automatically by TrueFFS.
All of the routines assigned to the socket structure member functions by the
registration routine are defined in the stub socket driver module. However, only
the rfaSocketInit( ) and rfaSetWindow( ) routines are incomplete. When you are
editing the stub file, note the #error and /* TODO */ comments in the code. These
indicate where and how you modify the code.
Following is a list of all of the routines assigned by the registration routine, along
with a description of how each was implemented in the stub file. The two routines
that require your attention are listed with descriptions of how they are to be
implemented.
NOTE: More detailed information on the functionality of each routine is provided
This routine always returns TRUE in RFA environments, since the device is not
removable. Implementation is complete in the stub file.
rfaVccOn
317
VxWorks 5.5
Programmers Guide
Therefore, do not delete this routine. While the implementation in the stub file is
complete, you may want to add code, as described below.
While When switching Vpp on, the VppOn( ) function must not return until Vpp
has stabilized at the proper voltage. If necessary, your VppOn( ) function should
delay execution with an idle loop or with a call to the flDelayMsec( ) routine, until
the Vpp has stabilized.
rfaVppOff
This routine sets current window hardware attributes: base address, size, speed
and bus width. The requested settings are given in the vol.window structure. If it
is not possible to set the window size requested in vol.window.size, the window
size should be set to a larger value, if possible. In any case, vol.window.size should
contain the actual window size (in 4 KB units) on exit.
318
8
Flash Memory Block Device Driver
For more information, see nrfaSetWindow, p.321 and Socket Windowing and Address
Mapping, p.322.
!
CAUTION: On systems with multiple socket drivers (to handle multiple flash
devices), make sure that the window base address is different for each socket. In
addition, the window size must be taken into account to verify that the windows
do not overlap.
rfaSetMappingContext
TrueFFS calls this routine to set the window mapping register. Because
board-resident flash arrays usually map the entire flash in memory, they do not
need this function. In the stub file it is a wrapper, thus implementation is complete.
rfaGetAndClearChangeIndicator
Always return FALSE in RFA environments, since the device is not removable.
This routine is complete in the stub file.
rfaWriteProtected
services that control power to the socket (be it PCMCIA, RFA, or any other
type)
This section describes details about socket registration, socket member functions,
and the windowing and address mapping set by those functions. This information
is not necessary to port the stub RFA file; however, it may be useful for writers of
PCMCIA socket drivers.
319
VxWorks 5.5
Programmers Guide
Socket Registration
The first task the registration routine performs is to assign drive numbers to the
socket structures. This is fully implemented in the stub file. You only need to be
aware of the drive number when formatting the drives (8.6.1 Specifying the Drive
Number, p.307 ).
The drive numbers are index numbers into a pre-allocated array of FLSocket
structures. The registration sequence dictates the drive number associated with a
drive, as indicated in the first line of code from the rfaRegister( ) routine:
FLSocket vol = flSockeOf (noOfDrives);
Here, noOfDrives is the running count of drives attached to the system. The
function flSocketOf( ) returns a pointer to socket structure, which is used as the
volume description and is incremented by each socket registration routine called
by the system. Thus, the TrueFFS core in the socket structures are allocated each of
the (up to) 5 drives supported for the system.2 When TrueFFS invokes the routines
that you implement to handle its hardware interface needs, it uses the drive
number as an index into the array to access the socket hardware for a particular
flash device.
rfaCardDetected This routine reports whether there is a flash memory card in the
PCMCIA slot associated with this device. For non-removable media, this routine
should always return TRUE. Internally, TrueFFS for Tornado calls this function
every 100 milliseconds to check that flash media is still there. If this function
returns FALSE, TrueFFS sets cardChanged to TRUE.
rfaVccOn TrueFFS can call this routine to turn on Vcc, which is the operating
voltage. For the flash memory hardware, Vcc is usually either 5 or 3.3 Volts. When
the media is idle, TrueFFS conserves power by turning Vcc off at the completion of
an operation. Prior to making a call that accesses flash memory, TrueFFS uses this
function to turn the power back on again.
However, when socket polling is active, a delayed Vcc-off mechanism is used, in
which Vcc is turned off only after at least one interval has passed. If several
flash-accessing operations are executed in rapid sequence, Vcc remains on during
the sequence, and is turned off only when TrueFFS goes into a relatively idle state.
320
8
Flash Memory Block Device Driver
rfaVccOff TrueFFS can call this routine to turn off the operating voltage for the
flash memory hardware. When the media is idle, TrueFFS conserves power by
turning Vcc off. However, when socket polling is active, Vcc is turned off only after
a delay. Thus, if several flags accessing operations are executed in rapid sequence,
Vcc is left on during the sequence. Vcc is turned off only when TrueFFS goes into
a relatively idle state. Vcc is assumed to be ON constantly in RFA environments.
rfaVppOn This routine is not optional, and must always be implemented. TrueFFS
calls this routine to apply Vpp, which is the programming voltage. Vpp is usually
12 Volts to the flash chip. Because not all flash chips require this voltage, the
member is included only if SOCKET_12_VOLTS is defined.
Vpp must be known to be good on exit and is assumed to be ON constantly in RFA
environments.
NOTE: The macro SOCKET_12_VOLTS is only alterable by users that have source
rfaVppOff TrueFFS calls this routine to turn off a programming voltage (Vpp,
usually 12 Volts) to the flash chip. Because not all flash chips require this voltage,
the member is included only if SOCKET_12_VOLTS is defined. This routine is not
optional, and must always be implemented. Vpp is assumed to be ON constantly
in RFA environments.
rfaSocketInit TrueFFS calls this function before it tries to access the socket. TrueFFS
uses this function to handle any initialization that is necessary before accessing the
socket, especially if that initialization was not possible at socket registration time.
For example, if you did no hardware detection at socket registration time, or if the
flash memory medium is removable, this function should detect the flash memory
medium and respond appropriately, including setting cardDetected to FALSE if it
is missing.
rfaSetWindow TrueFFS uses window.base to store the base address of the memory
window on the flash memory, and window.size to store the size of the memory
window. TrueFFS assumes that it has exclusive access to the window. That is, after
it sets one of these window characteristics, it does not expect your application to
directly change any of them, and could crash if you do. An exception to this is the
mapping register. Because TrueFFS always reestablishes this register when it
accesses flash memory, your application may map the window for purposes other
than TrueFFS. However, do not do this from an interrupt routine.
321
VxWorks 5.5
Programmers Guide
register. This routine performs the sliding action by setting the mapping register to
an appropriate value. Therefore, this routine is meaningful only in environments
such as PCMCIA, that use the sliding window mechanism to view flash memory.
Flash cards in the PCMCIA slot use this function to access/set a mapping register
that moves the effective flash address into the hosts memory window. The
mapping process takes a card address, an offset in flash, and produces real
address from it. It also wraps the address around to the start of flash if the offset
exceeds flash length. The latter is the only reason why the flash size is a required
entity in the socket driver. On entry to setMappingContext,
vol.window.currentPage is the page already mapped into the window (meaning
that it was mapped in by the last call to setMappingContext).
indication and clears it. It serves as a basis for detecting media-change events. If
you have no such hardware capability, return FALSE for this routine (set this
function pointer to NULL).
rfaWriteProtected TrueFFS can call this routine to get the current state of the
medias write-protect switch (if available). This routine returns the write-protect
state of the media, if available, and always returns FALSE for RFA environments.
For more information, see 8.7.1 Write Protecting Flash, p.309.
The PCMCIA bus could be entirely visible in the hosts address range.
Only a segment of the PCMCIA address range could be visible in the hosts
address space.
Only a segment of the hosts address space could be visible to the PCMCIA.
To support these concepts, PCMCIA specified the use of a window base register
that may be altered to adjust the view from the window. In typical RFA scenarios,
322
8
Flash Memory Block Device Driver
where the device logic is NOR, the window size is that of the amount of flash on
the board. In the PCMCIA situation, the window size is implementation-specific.
The book PCMCIA Systems Architecture by Don Anderson provides an good
explanation of this concept, with illustrations.
Common Functionality
Both drivers support 8 and 16 bit devices, and 8- and 16-bit wide interleaves.
Configuration macros (described in the code) are used to control these and other
configuration issues, and must be defined specifically for your system. If
modifications are made to the code, it must be rebuilt. Noteworthy are the
following macros:
323
VxWorks 5.5
Programmers Guide
INTERLEAVED_MODE_REQUIRES_32BIT_WRITES
Must be defined for systems that have 16-bit interleaves and require support
for the Write to Buffer command.
SAVE_NVRAM_REGION
Excludes the last erase block on each flash device in the system that is used by
TrueFFS; this is so that the region can be used for Non-Volatile Storage of boot
parameters.
CFI_DEBUG
Makes the driver verbose by using the I/O routine defined by DEBUG_PRINT.
BUFFER_WRITE_BROKEN
Introduced to support systems that registered a buffer size greater than 1, yet
could not support writing more than a byte or word at a time. When defined,
it forces the buffer size to 1.
DEBUG_PRINT
The MTD defined in cfiscs.c supports flash components that follow the CFI/SCS
specification. CFI stands for Common Flash Interface. SCS stands for Scalable
Command Set. CFI is a standard method for querying flash components for their
characteristics. SCS is a second layer built on the CFI specification. This lets a single
MTD handle all CFI/SCS flash technology in a common manner.
The joint CFI/SCS specification is currently adopted by Intel Corporation and
Sharp Corporation for all new flash components starting in 1997.
The CFI document can be downloaded from:
http://www.intel.com/design/flcomp/applnots/292204.htm
or can be found by searching for CFI at:
http://www.intel.com/design
Define INCLUDE_MTD_CFISCS in your BSPs sysTffs.c file to include this MTD in
TrueFFS for Tornado.
324
8
Flash Memory Block Device Driver
In some of the more recent target boards we have observed that non-volatile RAM
circuitry does not exist, and that the BSP developers have opted to use the high end
of flash for this purpose. The last erase block of each flash part is used to make up
this region. The CFISCS MTD supports this concept by providing the compiler
constant SAVE_NVRAM_REGION. If this is defined, the driver reduces the devices
size by the erase block size times the number of devices; this results in the NVRAM
region being preserved and never over-written. The ARM BSPs, in particular, use
flash for NVRAM and for the boot image.
In AMD and Fujitsu devices, the flexible sector architecture, also called boot block
devices, is only supported when erasing blocks. However, the TrueFFS core and
translation layers have no knowledge of the subdivision within the boot block
because the MTD presents this division transparently. According to the data sheet
for the 29LV160 device, it is comprised of 35 sectors. However, the 4 boot block
sectors appear to the core and translation layer as yet another, single (64 KB) sector.
Thus, the TrueFFS core detects only 32 sectors. Consequently, the code that
supports boot images also has no knowledge of the boot block, and cannot provide
direct support for it.
The AMD and Fujitsu devices include the concept of Top and Bottom boot devices.
However, the CFI interrogation process does not provide a facility for
distinguishing between the two. Thus, in order to determine the boot block type
the driver embeds the JEDEC device IDs in it. This limits the number of supported
devices to the ones that are registered in it, requiring verification that the device in
use is listed in the registry.
The MTD defined in i28f016.c supports Intel 28F016SA and Intel 28F008SV flash
components. Any flash array or card based on these chips is recognized and
supported by this MTD. This MTD also supports interleaving factors of 2 and 4 for
BYTE-mode 28F016 component access.
325
VxWorks 5.5
Programmers Guide
The MTD defined in I28F008.c supports the Intel 28F008SA, Intel 28F008SC, and
Intel 28F016SA/SV (in 8-mbit compatibility mode) flash components. Any flash
array or card based on these chips is recognized and supported by this MTD.
However, the WORD-mode of 28F016SA/SV is not supported (BYTE-mode only).
This MTD also supports all interleaving factors (1, 2, 4, ...). Interleaving of more
than 4 is recognized, although the MTD does not access more than 4 flash parts
simultaneously. The list of supported flash media includes the following:
The MTD defined in amdmtd.c (8-bit) supports AMD flash components of the
AMD Series-C and Series-D flash technology family, as well as the equivalent
Fujitsu flash components. The flash types supported are:
326
8
Flash Memory Block Device Driver
Any flash array or card based on these chips is recognized and supported by this
MTD. The MTD supports interleaving factors of 1, 2, and 4. The list of supported
flash media includes the following:
Define INCLUDE_MTD_AMD in your BSPs sysTffs.c file to include the 8-bit MTD
in TrueFFS for Tornado.
327
VxWorks 5.5
Programmers Guide
provided in source form as examples of how one might write an MTD (in
installDir/target/src/drv/tffs). This section provides additional information about
writing them.
In the process of creating a logical block device for a flash memory array, TrueFFS
tries to match an MTD to the flash device. To do this, TrueFFS calls the
identification routine from each MTD until one reports a match. The first reported
match is the one taken. If no MTD reports a match, TrueFFS falls back on a default
read-only MTD that reads from the flash device by copying from the socket
window.
The MTD identification routine is guaranteed to be called prior to any other
routine in the MTD. An MTD identification routine is of the following format:
FLStatus xxxIdentify(FLFlash vol)
Within an MTD identify routine, you must probe the device to determine its type.
How you do this depends on the hardware. If the type is not appropriate to this
MTD, return failure. Otherwise, set the members of the FLFlash structure listed
below.
The identification routine for every MTD must be registered in the mtdTable[ ]
defined in installDir/target/src/drv/tffs/tffsConfig.c. Each time a volume is
mounted, the list of identification routines is traversed to find the MTD suitable for
the volume. This provides better service for hot-swap devices; no assumption is
made about a previously identified device being the only device that will work for
a given volume.
Device identification can be done in a variety of ways. If your device conforms to
Joint Electronic Device Engineering Council (JEDEC) or Common Flash Interface
(CFI) standards, you can use their identification process. You might want your
MTD to identify many versions of the device, or just simply one.
328
8
Flash Memory Block Device Driver
At the end of the identification process, the ID routine needs to set all data
elements in the FlFlash structure, except the socket member. The socket member
is set by functions internal to TrueFFS. The FLFlash structure is defined in
installDir/h/tffs/flflash.h. Members of this structure are described as follows:
type
The JEDEC ID for the flash memory hardware. This member is set by the MTDs
identification routine.
8
erasableBlockSize
The size, in bytes, of an erase block for the attached flash memory hardware. This
value takes interleaving into account. Thus, when setting this value in an MTD, the
code is often of the following form:
vol.erasableBlockSize = aValue * vol.interleaving;
Where aValue is the erasable block size of a flash chip that is not interleaved with
another.
chipSize
The size (storage capacity), in bytes, of one of the flash memory chips used to
construct the flash memory array. This value is set by the MTD, using your
flFitInSocketWindow( ) global routine.
noOfChips
The number of flash memory chips used to construct the flash memory array.
interleaving
The interleaving factor of the flash memory array. This is the number of devices
that span the data bus. For example, on a 32-bit bus we can have four 8-bit devices
or two 16-bit devices.
flags
Bits 0-7 are reserved for the use of TrueFFS (it uses these flags to track things such
as the volume mount state). Bits 8-15 are reserved for the use of the MTDs.
329
VxWorks 5.5
Programmers Guide
mtdVars
This field, if used by the MTD, is initialized by the MTD identification routine to
point to a private storage area. These are instance-specific. For example, suppose
you have an Intel RFA based on the I28F016 flash part; suppose you also have a
PCMCIA socket into which you decide to plug a card that has the same flash part.
The same MTD is used for both devices, and the mtdVars are used for the variables
that are instance-specific, so that an MTD may be used more than once in a system.
socket
This member is a pointer to the FLSocket structure for your hardware device. This
structure contains data and pointers to the socket layer functions that TrueFFS
needs to manage the board interface for the flash memory hardware. The functions
referenced in this structure are installed when you register your socket driver (see
8.10 Writing Socket Drivers, p.314). Further, because TrueFFS uses these socket
driver functions to access the flash memory hardware, you must register your
socket driver before you try to run the MTD identify routine that initializes the bulk
of this structure.
map
A pointer to the flash memory map function, the function that maps flash into an
area of memory. Internally, TrueFFS initializes this member to point to a default
map function appropriate for all NOR (linear) flash memory types. This default
routine maps flash memory through simple socket mapping. Flash should replace
this pointer to the default routine with a reference to a routine that uses
map-through-copy emulation.
read
A pointer to the flash memory read function. On entry to the MTD identification
routine, this member has already been initialized to point to a default read function
that is appropriate for all NOR (linear) flash memory types. This routine reads
from flash memory by copying from a mapped window. If this is appropriate for
your flash device, leave read unchanged. Otherwise, the MTD identify routine
should update this member to point to a more appropriate function.
330
8
Flash Memory Block Device Driver
write
A pointer to the flash memory write function. Because of the dangers associated
with an inappropriate write function, the default routine for this member returns
a write-protect error. The MTD identification routine must supply an appropriate
function pointer for this member.
erase
A pointer to the flash memory erase function. Because of the dangers associated
with an inappropriate erase function, the default routine for this member returns
a write-protect error. The MTD identification routine must supply an appropriate
function pointer for this member.
setPowerOnCallback
A pointer to the function TrueFFS should execute after the flash hardware device
powers up. TrueFFS calls this routine when it tries to mount a flash device. Do not
confuse this member of FLFlash with the powerOnCallback member of the
FLSocket structure. For many flash memory devices, no such function is necessary.
However, this member is used by the MTD defined in
installDir/target/src/drv/tffs/nfdc2048.c.
Return Value
The identification routine must return flOK or an appropriate error code defined in
flbase.h. The stub provided is:
FLStatus myMTDIdentification
(
FLFlash vol
)
{
/* Do what is needed for identification */
/* If identification fails return appropriate error */
return flOK;
}
After setting the members listed above, this function should return flOK.
331
VxWorks 5.5
Programmers Guide
Call Sequence
Upon success, the identification routine updates the FLFlash structure, which also
completes the initialization of the FLSocket structure referenced within this
FLFlash structure.
Figure 8-3
tffsDevCreate( )
flMountVolume( )
flMount( )
flIdentifyFlash( )
aaaIdentify( )
bbbIdentify( )
cccIdentify( )
...
332
8
Flash Memory Block Device Driver
{
/* implement function */
}
When writing these functions, you probably want to use the MTD helper functions
flNeedVpp( ), flDontNeedVpp( ), and flWriteProtected( ). The interfaces for these
routines are as follows:
FLStatus flNeedVpp(FLSocket vol)
void flDontNeedVpp(FLSocket vol)
FLBoolean flWriteProtected(FLSocket vol)
Use flNeedVpp( ) if you need to turn on the Vpp (programming voltage) for the
chip. Internally, flNeedVpp( ) bumps a counter, FLSocket.VppUsers, and then
calls the function referenced in FLSocket.VppOn. After calling flNeedVpp( ),
check its return status to verify that it succeeded in turning on Vpp.
When done with the write or erase that required Vpp, call flDontNeedVpp( ) to
decrement the FLSocket.VppUsers counter. This FLSocket.VppUsers counter is
part of a delayed-off system. While the chip is busy, TrueFFS keeps the chip
continuously powered. When the chip is idle, TrueFFS turns off the voltage to
conserve power. 3
Use flWriteProtected( ) to test that the flash device is not write protected. The MTD
write and erase routines must not do any flash programming before checking that
writing to the card is allowed. The boolean function flWriteProtected( ) returns
TRUE if the card is write-protected and FALSE otherwise.
Read Routine
If the flash device can be mapped directly into flash memory, it is generally a
simple matter to read from it. TrueFFS supplies a default function that performs a
3. An MTD does not need to touch Vcc. TrueFFS turns Vcc on before calling an MTD function.
333
VxWorks 5.5
Programmers Guide
remap, and simple memory copy, to retrieve the data from the specified area.
However, if the mapping is done through a buffer, you must provide your own
read routine.
Write Routine
The write routine must write a given block at a specified address in flash. Its
arguments are a pointer to the flash device, the address in flash to write to, a
pointer to the buffer that must be written, and the buffer length. The last parameter
is boolean, and if set to TRUE implies that the destination has not been erased prior
to the write request. The routine is declared as static since it is only called from the
volume descriptor. The stub provided is:
static FLStatus myMTDWrite
(
FLFlash vol,
CardAddress address,
const void FAR1 *buffer,
int length,
FLBoolean overwrite
)
{
/* Write routine */
return flOK;
}
Always map the card address provided to a flashPtr before you write.
When implementing the write routine, iterate through the buffer in a way that is
appropriate for your environment. If writes are permitted only on word or double
word boundaries, check to see whether the buffer address and the card address are
so aligned. Return an error if they are not.
The correct algorithms usually follow a sequence in which you:
Loop on the status register until either the status turns OK or you time out.
Device data sheets usually provide flow charts for this type of algorithm. AMD
devices require an unlock sequence to be performed as well.
334
8
Flash Memory Block Device Driver
The write routine is responsible for verifying that what was written matches the
content of the buffer from which you are writing. The file flsystem.h has
prototypes of compare functions that can be used for this purpose.
Erase Routine
The erase routine must erase one or more contiguous blocks of a specified size.
This routine is given a flash volume pointer, the block number of the first erasable
block and the number of erasable blocks. The stub provided is:
Static FLStatus myMTDErase
(
FLFlash vol,
int
firstBlock,
int
numOfBlocks
)
{
volatile UINT32 * flashPtr;
int
iBlock;
if (flWriteProtected(vol.socket))
return flWriteProtected;
for (iBlock = firstBlock; iBlock < iBlock + numOfBlocks; Iblock++)
{
flashPtr = vol.map (&vol, iBlock * vol.erasableBlockSize, 0);
/* Perform erase operation here */
/* Verify if erase succeeded */
/* return flWriteFault if failed*/
}
return flOK;
}
As input, the erase can expect a block number. Use the value of the
erasableBlockSize member of the FLFlash structure to translate this block number
to the offset within the flash array.
335
VxWorks 5.5
Programmers Guide
Once you define your MTD component in the 00tffs.cdf file, it appears in the
project facility the next time you run Tornado.
For a command-line configuration and build, you can include the MTD component
simply by defining it in the socket driver file, sysTffs.c, as follows:
#define INCLUDE_MTD_USR
Add your MTD definition to the list of those defined between the conditional
clause, as described in nConditional Compilation, p.305. Then, define the correct
translation layer for your MTD. If both translation layers are defined in the socket
driver file, undefine the one you are not using. If both are undefined, define the
correct one. For other examples, see the type-sysTffs.c files in
installDir\target\src\drv\tffs\sockets.
!
CAUTION: Be sure that you have the correct sysTffs.c file before changing the
defines. For more information, see 8.10.1 Porting the Socket Driver Stub File, p.315.
336
8
Flash Memory Block Device Driver
/* INCLUDE_MTD_AMD */
#ifdef INCLUDE_MTD_CDSN
cdsnIdentify,
#endif
/* INCLUDE_MTD_CDSN */
#ifdef INCLUDE_MTD_DOC2
doc2Identify,
#endif
/* INCLUDE_MTD_DOC2 */
#ifdef INCLUDE_MTD_CFISCS
cfiscsIdentify,
#endif
/* INCLUDE_MTD_CFISCS */
};
If you write a new MTD, list its identification routine in mtdTable[ ]. For example:
#ifdef INCLUDE_MTD_USR
usrMTDIdenitfy,
#endif
/* INCLUDE_MTD_USR */
337
VxWorks 5.5
Programmers Guide
To promote more efficient data retrieval, TrueFFS uses a flexible allocation strategy,
which clusters related data into a contiguous area in a single erase unit. These
clusters might be, for example, the blocks that comprise the sectors of a file.
TrueFFS follows a prioritized procedure for attempting to cluster the related data.
In this order:
(1) First, it tries to maintain a pool of physically consecutive free blocks that
are resident in the same erase unit.
(2) If that fails, it then tries to assure that all the blocks in the pool reside in the
same erase unit.
(3) If that fails, it finally tries to allocate a pool of blocks in the erase unit that
has the most space available.
Benefits of Clustering
Clustering related data in this manner has several benefits, listed and described
below.
Allows Faster Retrieval Times. For situations that require TrueFFS to access
flash through a small memory window, clustering related data minimizes the
number of calls needed to map physical blocks into the window. This allow faster
retrieval times for files accessed sequentially.
338
8
Flash Memory Block Device Driver
collection is faster.
Localizes Static File Blocks. Localizing blocks that belong to static files
significantly facilitates transferring these blocks when the wear-leveling algorithm
decides to move static areas.
One of the characteristics of flash memory that differs considerably from the more
common magnetic-medium mechanical disks is the way in which it writes new
data to the medium. When using traditional magnetic storage media, writing new
data to a previously written storage area simply overwrites the existing data,
essentially obliterating it; whereas flash does not. This section describes how flash
reads from, and writes to, memory.
Reading the data from a block is straightforward. The file system requests the
contents of a particular block. In response, TrueFFS translates the block number
into flash memory coordinates, retrieves the data at those coordinates, and returns
the data to the file system.
339
VxWorks 5.5
Programmers Guide
If the write request is to an area of flash that already contains data, TrueFFS finds
a different, writable area of flash insteadone that is already erased and ready to
receive data. TrueFFS then writes the new data to that free area. After the data is
safely written, TrueFFS updates its block-to-flash mapping structures, so that the
block now maps to the area of flash that contains the modified data. This mapping
information is protected, even during a fault. For more information on fault
recovery and mapping information, see Recovering Mapping Information, p.344.
Erasing Units
Once data is written to an area of flash memory, modifying blocks leaves behind
block-sized regions of flash memory that no longer contain valid data. These
regions are also unwritable until erased. However, the erase cycle does not operate
on individual bytes or even blocks. Erasure is limited to much larger regions called
erase units. The size of these erase units depends on the specific flash technology,
but a typical size is 64 KB.
To reclaim (erase) blocks that no longer contain valid data, TrueFFS uses a
mechanism called garbage collection. This mechanism copies all the valid data
blocks, from a source erase unit, into another erase unit known as a transfer unit.
TrueFFS then updates the block-to-flash map and afterward erases the old (erase)
unit. The virtual block presented to the outside world still appears to contain the
same data even though that data now resides in a different part of flash.
For details on the algorithm used to trigger garbage collection, see Garbage
Collection, p.342. For information about garbage collection and fault recovery, see
Recovering During Garbage Collection, p.344.
340
8
Flash Memory Block Device Driver
Over-Programming
Wear Leveling
341
VxWorks 5.5
Programmers Guide
Garbage Collection
342
8
Flash Memory Block Device Driver
TrueFFS can recover from the fault in all cases except when new data is being
written to flash for the first time. This new data will be lost. However, once data is
safely written to flash, it is essentially immune to power failures. All data already
resident in flash is recoverable, and the file and directory structures of the disk are
retained. In fact, the negative consequence of a power interruption or fault is the
need to restart any incomplete garbage collection operation. This section describes
fault occurrence and fault recovery in TrueFFS.
343
VxWorks 5.5
Programmers Guide
After a fault, any garbage collection in process at the time of the failure must be
restarted. Garbage area is space that is occupied by sections that have been deleted
by the host. TrueFFS reclaims garbage space by first moving data from one transfer
unit to another, and then erasing the original unit.
If consistent flash failures prevent the necessary write operations to move data, or
if it is not possible to erase the old unit, the garbage collection operation fails. To
minimize a failure of the write part of the transfer, TrueFFS formats the flash
medium to contain more than one transfer unit. Thus, if the write to one transfer
unit fails, TrueFFS retries the write using a different transfer unit. If all transfer
units fail, the medium no longer accepts new data and becomes a read-only device.
This does not, however, have a direct effect on the user data, all of which is already
safely stored.
In some cases, sections of the flash medium are found to be unusable when flash is
first formatted. Typically, this occurs because those sections are not erasable. As
long as the number of bad units does not exceed the number of transfer units, the
medium is considered usable as a whole and can be formatted. The only noticeable
adverse effect is the reduced capacity of the formatted flash medium.
344
9
VxDCOM Applications
COM Support and Optional Component VxDCOM
9.1 Introduction
VxDCOM is the technology that implements COM and distributed COM (DCOM)
on VxWorks. The name VxDCOM refers both to this technology and to the optional
product. The VxDCOM optional product adds DCOM capabilities to the basic
COM support that is included in the standard VxWorks facilities.
COM stands for the Component Object Model, which is a binary specification for
component-based object communication. The Wind River VxDCOM technology
was designed to significantly facilitate the creation of both COM and DCOM
components, by automating much of the boiler-plate code. Thus, VxDCOM
enables users to easily write distributed object applications for use in real-time
embedded systems software.
The VxDCOM documentation assumes working knowledge of the COM
technology, and focuses on the VxDCOM facilities that are used to create server
applications for execution on VxWorks. The documentation comprises both this
chapter and the Tornado Users Guide: Building COM and DCOM Applications. The
latter includes a step-by-step overview of how to create and build a VxDCOM
application. It covers procedural information for running the VxDCOM wizard,
descriptions of the generated output, and the process of building and deploying
VxDCOM applications.
This chapter covers the following topics, which are primarily reference material
and programming issues:
345
VxWorks 5.5
Programmers Guide
The demo example used in this chapter is a DCOM server application that includes
both a Visual Basic and a C++ client. Source for this demo is located in the
installDir/host/src/vxdcom/demo/MathDemo directory.
346
9
VxDCOM Applications
COM Interfaces
(WOTL) and are shipped with VxDCOM. For the API definitions, see comLib.h
and dcomLib.h.
As a developer, you typically define your own custom interfaces. Interface
definitions must conform to the COM specification, including descriptive
attributes and specifications for the interface, its methods, the method parameters,
and the return type (see The Interface Definition, p.369). By strictly adhering to this
specification, the COM interface becomes a contractual agreement between client
and server, and enables the communication protocol to function properly.
NOTE: Interface definitions are pure prototypes, similar to abstract C++ classes
that contain only pure virtual methods. In fact, the Wind Object Template Library
(WOTL), which you use to write VxDCOM applications, implements interfaces in
exactly this manner.
The contract between client and server is to provide a service; but it is not (and
need not be) a guarantee how that service is implemented. The implementation
details are hidden in the CoClass.
CoClasses
CoClasses are the classes that declare the interfaces and implement the interface
methods. The CoClass definition includes a declaration of all the interfaces that it
347
VxWorks 5.5
Programmers Guide
agrees to support. The CoClass implementation code must implement all of the
methods of all of the interfaces declared by that CoClass. When the CoClass is
instantiated, it becomes a COM component, or server. Client applications query for
interfaces services they need, and if your server supports those interfaces, then it
communicates with the client application through those interfaces.
Interface Pointers
In the COM model, the communication protocol between COM clients and servers
in handled using pointers to the COM interfaces. The interface pointers are used
to pass data between COM clients and COM servers. The client application uses
COM interface pointers to query COM servers for specific interfaces, to obtain
access to those interfaces, and to invoke the methods of the interfaces. Because the
COM specification and communication protocol is based on these pointers, it is
considered to be a binary standard.
Because COM technology uses a binary standard, it is theoretically possible for
COM components to be written in any language, and to run on any operating
system, while still being able to communicate. In this way, COM technology offers
great flexibility and component reusability to software developers, especially
those wanting to port applications to different operating systems.
NOTE: Currently, the only languages supported for COM servers under VxDCOM
are C and C++; for DCOM servers, only C++ is currently supported.
VxDCOM Tools
348
9
VxDCOM Applications
349
VxWorks 5.5
Programmers Guide
True CoClasses: These are true COM classes, meaning that they have registered
CLSIDs and are instantiated through class factories. The CComCoClass template
class is used to declare these classes. This is the template class that the VxDCOM
wizard uses to generates skeleton code for the CoClass definitions for your COM
components.
Singleton classes: These are also true CoClasses, but for which there is only one
350
9
VxDCOM Applications
Lightweight classes: These are simple classes that are not technically true COM
classes. The template class has no associated CLSID (class identification value) and
thus, instantiates objects by using a default class-factory creation mechanism.
These can never be DCOM classes. To declare such a class, use the CComObject
class. You typically use these classes to create internal objects that enhance the
object-oriented functionality of your code.
The WOTL classes and class templates are described below, with details on how to
use them. WOTL classes are declared in the header
installDir/target/h/comObjLib.h. Some additional helper classes, such as
VxComBSTR, are defined in installDir/target/h/comObjLibExt.h. For more
information, see 9.11 Comparing VxDCOM and ATL Implementations., p.385.
9
CComObjectRoot is the base class for all VxDCOM classes and provides task-safe
IUnknown implementation, plus support for aggregatable object. To declare a
class that provides concrete implementations for one or more COM interfaces, the
class must inherit from both CComObjectRoot and the interfaces it implements.
The following example declares a class, CExample, that supports the interface
IExample and will implement its methods:
class CExample: public CComObjectRoot, public IExample
{
// Private instance data
public:
// Implementation, including IExample methods...
};
351
VxWorks 5.5
Programmers Guide
Example Definition
The example below shows this inheritance in the definition of the server CoClass
in the CoMathDemoImpl.h header file:
class CoMathDemoImpl
: public CComObjectRoot,
public CComCoClass<CoMathDemoImpl, &CLSID_CoMathDemo>
,public IMathDemo
,public IEvalDemo
{
// body of definition
};
The generated server implementation header for your application will include a
declaration for your CoClass that is similarly derived from CComObjectRoot,
from CComCoClass, your primary interface, and from any additional interfaces.
Note that WOTL supports multiple inheritance, which includes inheritance from
multiple interfaces; so your CoClass definition derives from each of the interfaces
it implements.
352
9
VxDCOM Applications
The CComCoClass class template creates its class instantiation based upon the
name of the CoClass and the CLSID. The &CLSID_CoMathDemo parameter, in
the CoMathDemo example code above, represents the GUID (globally unique
identifier) that identifies the CoClass. The CLSID for your CoClass is
CLSID_basename, and can be found in the basename_i.c file, generated by the widl
compilation. The parameter you would use is thus &CLSID_basename.
NOTE: The CLSID_basename (class ID) is declared as a const, and represents the
GUID generated for the CoClass from the compilation of the .idl file.
CLSID_basename is used in the server header and implementation files, and also in
the client implementation file. Use this const when referencing the CLSID (class
ID) for your CoClass.
353
VxWorks 5.5
Programmers Guide
354
9
VxDCOM Applications
You can include other header files as necessary, depending upon your
implementation code. For example, the CoMathDemoImpl.h header file looks like
this:
/* CoMathDemoImpl.h -- auto-generated COM class header */
#include "comObjLib.h"
#include "CoMathDemo.h"
#include <string>
STDMETHODCALLTYPE
STDMETHODIMP
STDMETHODIMP_(type)
STDMETHOD(method)
STDMETHOD_(type,method)
STDCALL
HRESULT STDMETHODCALLTYPE
type STDMETHODCALLTYPE
virtual HRESULT STDMETHODCALLTYPE method
virtual type STDMETHODCALLTYPE method
These macros are used when mapping interface method definitions across .idl
files, header files, and implementation files. The details of this process are
described in the sections that follow. Table 9-1 provides a quick reference summary
of these mappings when reading the files.
355
VxWorks 5.5
Programmers Guide
Table 9-1
File
Meaning
wizard
basename.idl
IDL interface
definitions
basename.h
C++ interface
method prototypes
widl
basenameImpl.h
C++ CoClass
method prototypes
wizard
STDMETHOD(method) (parameters);
basenameImpl.cpp
C++ CoClass
method definitions
wizard
Using these macros, the interface method prototypes are mapped so that each
interface method definition, found in the .idl file, is defined as a virtual STDCALL
method that returns an HRESULT in the generated interface header file, basename.h.
Thus, the following definition syntax in the .idl file:
HRESULT routine_name (parameters);
becomes:
virtual HRESULT STDMETHODCALLTYPE routine_name(parameters) = 0;
So, for each interface method you define in the wizard, the final prototype in your
basenameImpl.h file is:
STDMETHOD(method) (parameters);
356
9
VxDCOM Applications
NOTE: If you edit the C++ header, basenameImpl.h, by hand, use the CoMathDemo
The header files generated for your CoClass will have similar interface map
definitions for your CoClass interfaces.1
1. The default IUnknown is always the first COM_INTERFACE_ENTRY in the table. Therefore, by definition, this entry must be derived from IUnknown or the static_cast( ) will fail.
357
VxWorks 5.5
Programmers Guide
application.
Values
0 = default, no authentication required
1 = no authentication required
2 = RPC_CN_AUTHN_LEVEL_CONNECT, which means that the
application must create a userId and password combination (using the
vxdcomUserAdd( ) API) so that incoming connections can be
authenticated.
Default
0
Values
TRUE = BSTRs are marshaled as counted strings (byte-arrays), in which
the first byte specifies the length of the string and the remaining bytes are
represented as ASCII characters.
FALSE = BSTRs are marshaled as BSTRs, that is as wide-character strings,
358
9
VxDCOM Applications
Default
TRUE
For more information, see 9.8.2 Configuring Client Priority Propagation on Windows,
p.375 .
validating the ObjectExporter port number after a system reboot. If this parameter
is set to zero, the port number is assigned dynamically.
Values
Range is 1025..65535 (short int values)
Default
65000
threadpool.
Values
Recommended range is 16K...128K
Default
16K
359
VxWorks 5.5
Programmers Guide
If data exceptions or stack exceptions are seen in tCOM tasks, use the browser to
check whether stack exhaustion is the cause. If it is, increase this value.
server threadpool.
Values
Range is 1..32
Default
5
VxDCOM starts up a number of initialized threads to speed up CoClass startup
times. Set this value to the average number of CoClasses running within the
system in order to speed up CoClass initialization.
server threadpool. For more information, see 9.8.3 Using Threadpools, p.376.
Values
Same range as that of the VxWorks task priority.
Default
150
360
9
VxDCOM Applications
The idlFile must be specified, with or without the .idl extension, which is assumed.
The other command-line switches are optional and are described below:
-I incDir
Add specified include directory to the header search path.
-noh
Do not generate header file.
-nops
Do not generate proxy/stub file.
-dep
Generate GNU-style dependency file.
-o outputDir
Write output from widl to the specified output directory.
361
VxWorks 5.5
Programmers Guide
WARNING: The widl tool only generates proxy/stub code for the interfaces
defined in the IDL file being compiled, and not for interfaces defined in imported
IDL files. To generate the proxy/stub code for those interfaces, you must
separately compile each IDL file that defines each of the required interfaces. The
exception to this rule is the IDL file, installDir\target\h\vxidl.idl, whose
proxy/stub code routines are supplied in DCOM support components.
Automation data types are simple types, interface pointers, VARIANTs, and
BSTRs as described below.
long, long *, int, int *, short, short *, char, char *, float, float *, double, double *
Integral values must be explicitly declared as one of the above or a pointer type
of one of the above.
IUnknown *, IUnknown **
Includes interface pointer types.
BSTR, BSTR *
BSTRs are wide-characters, which are 16-bit unsigned integers, used to create
character strings. Because COM APIs take wide-character strings as
parameters, strings are passed as BSTRs. You can modify the method of
reading and passing BSTRs in the configuration options parameters for
DCOM.
If you need to convert between the standard 8-bit ASCII type and the
wide-character strings, you can use the following routines:
comWideToAscii( )
Convert a wide-string to ASCII.
comAsciiToWide( )
Convert an ASCII string to a wide-string.
362
9
VxDCOM Applications
NOTE: VxDCOM also supports the ATL macros, OLE2T and T2OLE. However,
these macros call alloca( ), which uses memory from the current stack, meaning the
macros could consume an entire stack space if used inside a loop. It is
recommended that you use the VxComBSTR class, defined in comObjLibExt.h, as
an alternative. For details, see 9.11.7 VxComBSTR, p.391.
VARIANT, VARIANT *
Includes all values of the VARTYPE enumeration that are valid and supported
by VxDCOM in a VARIANT, including SAFEARRAY. For details on
SAFEARRAY support, see SAFEARRAY with VARIANTS, p.364.
If you use a non-automated type, then you will need to build a proxy DLL. You can
use midl to generate the DLL for you, and then you need to build it. For details on
registering proxy DLLs, see the Tornado Users Guide: Register Proxy DLLs on
Windows.
If you are using non-automation data types because you are using OPC interfaces,
then you need to also add the DCOM_OPC support component to your system, and
363
VxWorks 5.5
Programmers Guide
install the OPC Proxy DLL on windows. For more information, see the Tornado
Users Guide: OPC Program Support.
364
9
VxDCOM Applications
DCOM Support
Because Microsoft DCOM fragments packets that are over 4 KB in length, you
should only send SAFEARRAYs of 4 KB or less in size when sending a
SAFEARRAY from a Microsoft platform.
All interface methods require an HRESULT return type. HRESULTs are the 32-bit
return value from all ORPC methods and are used to handle RPC exceptions. By
using ORPC and HRESULT return types the COM technology can provide a
virtually transparent process of object communication from the developers
perspective. This is because, as long as client code returns an HRESULT, the client
application can access all objects, whether in-process or remote, in a uniform
transparent fashion. In fact, in-process servers communicate with client
applications by using C++ virtual method calls; whereas remote servers
communicate with client applications by using proxy/stub code and by invoking
local RPC services. However, this process is all transparent to the programmer,
because it happens automatically within the COM mechanism. The only difference
is that making a remote procedure call requires more overhead.2
The VxDCOM wizard automatically generates an HRESULT return type for all
methods you define. When writing your implementation code, you can refer to the
2. As there is no need to support cross-process (and local RPC) server models, VxDCOM
supports only the in-process and remote server models.
365
VxWorks 5.5
Programmers Guide
Meaning
S_OK
Success. (0x00000000)
E_OUTOFMEMORY
E_INVALIDARG
E_NOINTERFACE
E_ACCESSDENIED
E_UNEXPECTED
S_FALSE
False. (0x00000001)
366
9
VxDCOM Applications
The sample .idl file below is from the CoMathDemo demo program found in the
installDir/host/src/VxDCOM/MathDemo directory. It demonstrates the structure
of an .idl file that defines two interfaces, IMathDemo and IEvalDemo, a type
library, CoMathDemoLib, and a CoClass, CoMathDemo, that implements both
interfaces.
9
#ifdef _WIN32
import "unknwn.idl";
#else
import "vxidl.idl";
#endif
[
object,
oleautomation,
uuid(A972BFBE-B4A9-11D3-80B6-00C04FA12C4A),
pointer_default(unique)
]
interface IMathDemo:IUnknown
{
HRESULT pi([out,retval]double* value);
HRESULT acos ([in]double x, [out,retval]double* value);
HRESULT asin ([in]double x, [out,retval]double* value);
HRESULT atan([in]double x, [out,retval]double* value);
HRESULT cos([in]double x, [out,retval]double* value);
HRESULT cosh([in]double x, [out,retval]double* value);
HRESULT exp([in]double x, [out,retval]double* value);
HRESULT fabs([in]double x, [out,retval]double* value);
HRESULT floor ([in]double x, [out,retval]double* value);
HRESULT fmod([in]double x,[in]double y,[out,retval]double* value);
HRESULT log ([in]double x, [out,retval]double* value);
HRESULT log10 ([in]double x, [out,retval]double* value);
HRESULT pow ([in]double x,[in]double y,[out,retval]double* value);
HRESULT sin ([in]double x, [out,retval]double* value);
HRESULT sincos ([in]double x,
[out]double* sinValue,
[out]double* cosValue);
HRESULT sinh ([in]double x, [out,retval]double* value);
HRESULT sqrt ([in]double x, [out,retval]double* value);
HRESULT tan ([in]double x, [out,retval]double* value);
HRESULT tanh ([in]double x, [out,retval]double* value);
};
367
VxWorks 5.5
Programmers Guide
[
object,
oleautomation,
uuid(4866C2E0-B6E0-11D3-80B7-00C04FA12C4A),
pointer_default(unique)
]
interface IEvalDemo:IUnknown
{
HRESULT eval ([in]BSTR str, [out,retval]double* value);
HRESULT evalSubst ([in]BSTR str,
[in]double x,
[out,retval] double* value);
};
[
uuid(A972BFC0-B4A9-11D3-80B6-00C04FA12C4A),
version(1.0),
helpstring("CoMathDemo Type Library")
]
library CoMathDemoLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[
uuid(A972BFBF-B4A9-11D3-80B6-00C04FA12C4A),
helpstring("CoMathDemo Class")
]
coclass CoMathDemo
{
[default] interface IEvalDemo;
interface IMathDemo;
};
};
The following sections describe the syntax for the parts of an .idl file.
The import directive precedes the interface definition and specifies another .idl or
header file. These imported files contain definitions - such as typedefs, constant
declarations, and interface definitions - that can be referenced in the importing IDL
file. All interfaces inherit from the IUnknown interface; therefore all interface
definitions will conditionally include either the WIN32 header, unknwn.idl, or
vxidl.idl, which implements IUnknown under VxDCOM when running on a
target.
368
9
VxDCOM Applications
The interface definition in the .idl file specifies the actual contract between the client
application and server object. It describes the characteristics of each interface in an
interface header and an interface body. The syntax for an interface definition is:
[attributes] interface interfacename: base-interface {definitions};
Interface Header
The interface header is the section at the beginning of the interface definition. The
interface header comprises both the information that is enclosed in square
brackets, along with the keyword interface, followed by the interface name. The
information within the brackets is an attribute list describing characteristics that
apply to the interface as a whole. This information is global to the entire interface
(in contrast to attributes applied to interface methods.) The attributes describing
the interface - [object], [oleautomation], [uuid], and [pointer-default] - are the
standard generated attributes for VxDCOM interface definitions and are discussed
in 9.7.2 Definition Attributes, p.370.
Interface Body
The interface body is the section of the interface definition that is enclosed in C-style
braces ({ }). This section contains remote data types and method prototypes for the
interface. It can optionally include zero or more import lists, pragmas, constant
declarations, general declarations, and function declarators.
The type library and CoClass definitions follow the same syntax pattern as that of
an interface definition. The library definition syntax is:
[attributes] library libname {definitions};
369
VxWorks 5.5
Programmers Guide
When you run the D/COM Application Wizard, the generated interface definition
contains the following attributes as defaults. If you intend to modify any of them,
you must follow the language restrictions. (see Attribute Restrictions for VxDCOM,
p.372.)
3. This list specifies the full set of interfaces that the CoClass implements, both incoming and
outgoing.
370
9
VxDCOM Applications
[object]
The [object] attribute is an IDL extension that specifies the interface as a COM
interface (rather than an RPC interface). This attribute tells the IDL compiler to
generate all of the proxy/stub code specifically for a COM interface, and to
generate a type library for each library block defined within the .idl file (see Library
and CoClass Definitions, p.369). All VxDCOM interface definitions should specify
this attribute in the definition.
[oleautomation]
The [pointer_default] attribute specifies the default pointer attribute for all
pointers except pointers that appear as top-level parameters, such as individual
pointers used as function parameters; these automatically default to ref pointers.
The syntax for the [pointer_default] attribute is:
pointer_default (ptr | ref | unique)
In this case, the pointer attribute will override the default [pointer_default]
attribute that appears in the interface header. The [pointer_default] attribute is
371
VxWorks 5.5
Programmers Guide
optional in the .idl file, and is required only when a function returns an undefined
pointer type or when a function contains a parameter with more than one asterisk
(*).
[uuid]
The only interface attributes auto-generated for VxDCOM interface definitions are
[object], [oleautomation], [pointer-default], and [uuid]. Restrictions on using
interface attributes with VxDCOM are summarized as follows:
VxDCOM does not support all interface types; thus, any attributes that are
defined for non-supported interface types would not be applicable. For
example, because VxDCOM does not support IDispatch, the [dual] attribute
cannot be used.
For a complete list of interface attributes, their meanings, and valid combinations,
see the Microsoft documentation.
372
9
VxDCOM Applications
inheritance. A derived COM interface has a different UUID but inherits the
interface member functions, status codes, and interface attributes of the base
interface.
373
VxWorks 5.5
Programmers Guide
The generated definition in your server implementation file would look similar
with the first argument reflecting your server name.
This value indicates that the server will always run at a given priority. This
priority is assigned on a per object basis at object registration time, for
example, as a parameter to the AUTOREGISTER_COCLASS macro.
PS_CLNT_PROPAGATED
This value indicates that the worker thread serving the remote invocation
should run at the priority of client issuing the request. This effectively
provides similar semantics to invoking an operation on the same thread on the
same node.
PS_DEFAULT
This value indicates that a default priority can be specified that will be applied
to worker threads when the incoming request does not contain a propagated
priority. In the PS_CLNT_PROPAGATED case a value of -1 indicates that the
default server priority (configured in project facility) should be used when the
VXDCOM_ORPC_EXTENT is not present).
374
9
VxDCOM Applications
The third argument specifies the actual priority level to assign to either the server,
in the case of a PS_SVR_ASSIGNED scheme, or to the client, in the case of a
PS_CLNT_PROPAGATED scheme. In addition, it specifies when the
VXDCOM_ORPC_EXTENT (which contains the priority) is not present in the
request.
If the client is a VxWorks target, the client's priority is conveyed with the RPC
invocation to the server, using an ORPC extension mechanism specified in the
DCOM protocol definition. This causes the server to execute the method at the
same priority as the client task that invoked it.
9
375
VxWorks 5.5
Programmers Guide
Common Interfaces
Data Access
Alarms and Events
4. This is similar to a call center where a fixed number of operators service incoming calls. In
fact, there are Erlang calculators that can optimize the ideal number of operators given the
average call frequency and length.
376
9
VxDCOM Applications
Some of the OPC interface services use array and anonymous structure data types.
In order to support these types as per the OPC specification, these files are shipped
with some slight modifications in the form of conditional tags.
If you use OPC interfaces in your application, you may need to use
non-automation data types that require additional editing in the .idl file. For
details, see the Tornado Users Guide: Building COM and DCOM Applications.
The VxDCOM version of the OPC files import the installDir\target\h\vxidl.idl
file. This file defines interfaces required by an OPC application. For each of the
interfaces that are required from the OPC protocol, a proxy/stub interface is
required for VxDCOM to be able to remotely access an interface. In order to use
this proxy/stub code, you must add the DCOM_OPC support component, as
described in the Tornado Users Guide: Building COM and DCOM Applications.
The main program for all COM threads should be created using the VX_FP_TASK
option.
NOTE: Because COM is single-threaded, any COM objects that are spawned by the
Each implementation of an interface must have its own copy of the vtable,
including sections for the IUnknown method pointers. Using virtual inheritance
alters this layout and interferes with the COM interface mapping.
377
VxWorks 5.5
Programmers Guide
Therefore, when defining your COM and DCOM classes, do not use virtual
inheritance. For example, do not use this type of definition:
class CoServer : public virtual IFoo,
public virtual IBar
{
// class impl...
};
VxDCOM supports both in-process and remote servers. When you write the
servers, note that the main distinction between COM and DCOM components is
the version of COM API routines used. The DCOM version of a COM routine is
often appended with an Ex. In particular, you must use the CoCreateInstanceEx( )
version of the COM CoCreateInstance( ) routine for DCOM applications. For
details, see the COM and DCOM libraries as part of the Microsoft COM
documentation.
For the implementation details, see the CoMathDemo source files, which are well
commented.
Server Interfaces
378
9
VxDCOM Applications
The eval( ) method takes a BSTR, which contains a algebraic expression, str, and
returns the value as a double, value. Note that the variable that the eval( ) method
receives for evaluation is defined by the [in] parameter attribute and the value that
is returned is defined by both the [out] and [retval] parameter attributes. The
[retval] attribute allows this method to be used by languages that require a return
value. For more information on parameter attributes, see the Tornado Users
Reference: COM Tools.
This evalSubst( ) method similarly takes a BSTR containing an algebraic
expression and returns the value as a double. In addition, evalSubst( ) will also
substitute the variable x with a supplied value.
The implementation code indicates that both eval( ) and evalSubst( ) return an
HRESULT of S_OK when successful or E_FAIL when an error occurs in decoding the
expression.
379
VxWorks 5.5
Programmers Guide
Client Interaction
The first section of the client code contain #define and #include directives that are
mostly auto-generated by the VxDCOM wizard. The #include directives for the
alt*.* files for _WIN32 and the comOjbLib.h file were added manually because this
program uses CComBSTR. Similarly, you would add any necessary header files
for code that your program requires.
/* includes */
#ifdef _WIN32
#define _WIN32_WINNT 0x0400
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "CoMathDemo.w32.h"
#include <atlbase.h>
#include <atlimpl.cpp>
380
9
VxDCOM Applications
#else
#include "comLib.h"
#include "CoMathDemo.h"
#include "comObjLib.h"
#define mbstowcs comAsciiToWide
#endif
#include <stdio.h>
#include <iostream.h>
#include <math.h>
#ifdef _DCOM_CLIENT
#ifndef _WIN32
#include "dcomLib.h"
#endif
#endif
This section of the code creates the COM or DCOM object (component) by calling
the appropriate COM or DCOM routines, CoCreateInstance( ) or
CoCreateInstanceEx( ). The DCOM client requires additional initialization code
for security purposes. This creation and initialization code is auto-generated by the
wizard.
#define MAX_X 79
#define MAX_Y 25
int CoMathDemoClient (const char* serverName, const char* expression)
{
HRESULT
hr = S_OK;
double
result;
int
x;
int
y;
IUnknown * pItf;
IEvalDemo * pEvalDemo;
IMathDemo * pMathDemo;
381
VxWorks 5.5
Programmers Guide
If the client is DCOM client, this code is used. This section of code initializes
DCOM for this thread and creates the DCOM object on the target:
OLECHAR wszServerName [128];
// Convert the server name to a wide string.
mbstowcs (wszServerName, serverName, strlen (serverName) + 1);
// Initialize DCOM for this thread.
hr = CoInitializeEx (0, COINIT_MULTITHREADED);
if (FAILED (hr))
{
cout << "Failed to initialize DCOM\n";
return hr;
}
// This initizlizes security to none.
hr = CoInitializeSecurity (0, -1, 0, 0,
RPC_C_AUTHN_LEVEL_NONE,
RPC_C_IMP_LEVEL_IDENTIFY,
0, EOAC_NONE, 0);
if (FAILED (hr))
{
cout << "Failed to initialize security\n";
return hr;
}
The code following creates an MQI structure, which is used to query the COM
server object for the IID_IMathDemo interface. This is one of the two interfaces
defined by the CoMathDemo CoClass that is instantiated as the DCOM server.
When writing a typical DCOM client program with multiple interfaces you
include all your interface requests into the MQI and query them as one operation
(thus saving bandwidth). However, for the purposes of this demo we want to keep
the main body of the code the same; therefore, we only want the IUnknown for the
DCOM object at this point so that we can treat it the same way as a COM object
lower down:
MULTI_QI mqi [] = { {&IID_IEvalDemo, 0, S_OK} };
COSERVERINFO serverInfo = { 0, wszServerName, 0, 0 };
hr = CoCreateInstanceEx (CLSID_CoMathDemo,
0,
CLSCTX_REMOTE_SERVER,
&serverInfo,
1,
mqi);
if (SUCCEEDED (hr) && SUCCEEDED (mqi [0].hr))
{
cout << "Created CoMathDemo OK\n";
pItf = mqi [0].pItf;
382
9
VxDCOM Applications
}
else
{
cout << "Failed to create CoMathDemo, HRESULT=" <<
hex << cout.width (8) << mqi [0].hr << "\n";
return E_FAIL;
}
#endif
Query the IUnknown interface of the COM object to get an interface pointer to the
IMathDemo interface:
if (FAILED (pItf->QueryInterface (IID_IMathDemo, void**)&pMathDemo)))
{
cout << "Failed to create IMathDemo interface pointer, HRESULT="
<< hex << cout.width (8) << hr << "\n";
pEvalDemo->Release();
pItf->Release ();
return hr;
}
pItf->Release ();
This code calls the eval( ) method of the IEvalDemo interface, querying for it to
evaluate the given expression, which is passed to the client program from the
command line:5
cout << "Querying IEvalDemo interface\n";
CComBSTR str;
5. The expression is passed in as an array of char, but it is converted to a BSTR for marshaling
across to the COM server.
383
VxWorks 5.5
Programmers Guide
str = expression;
hr = pEvalDemo->eval(str, &result);
if (SUCCEEDED (hr))
{
cout << expression << "=" << result;
}
else
{
cout << "eval failed (" << hr << "," << result << ")\n";
}
pEvalDemo->Release ();
This code queries the IMathDemo interface to draw the sine and cosine graphs.
Note that it calls the pi( ), sin( ), and cos( ) methods of that interface:
printf("Querying IMathDemo interface\n");
double sinResult;
double cosResult;
double pi;
hr = pMathDemo->pi(&pi);
if (FAILED (hr))
return hr;
double step_x = (pi * 2.0) / ((double)MAX_X);
double scale_y = ((double)MAX_Y) / 2.0;
for (y = MAX_Y; y >= 0; y--)
{
for (x = 0; x < MAX_X; x++)
{
hr = pMathDemo->sin((double)x * step_x , &sinResult);
if (FAILED (hr))
return hr;
hr = pMathDemo->cos((double)x * step_x , &cosResult);
if (FAILED (hr))
return hr;
if ((int)((double)((sinResult + 1.0) * scale_y)) == y)
{
putchar('*');
}
else if ((int)((double)((cosResult + 1.0) * scale_y)) == y)
{
putchar('+');
}
else
{
putchar(' ');
}
}
putchar('\n');
}
pMathDemo->Release ();
384
9
VxDCOM Applications
#ifdef _DCOM_CLIENT
CoUninitialize ();
#endif
return hr;
}
This section of the code was added by the programmer for situations in which you
do not want C++ name mangling:
#else
extern "C"
{
int ComTest (char * server, char * exp)
{
return CoMathDemoClient (server, exp);
}
}
#endif
385
VxWorks 5.5
Programmers Guide
9.11.1 CComObjectRoot
VxDCOM implements CComObjectRoot directly, whereas ATL implements it as
a typedef of CComObjectRootEx.
CComObjectRoot always implements the aggregation pointer even if it is not
used.
Constructor
Constructor for the class. Initializes the ref count to 0 and provides a storage for the
aggregating outer interface, if required.
CComObjectRoot
(
IUnknown *
)
punk = 0
// aggregating outer
InternalAddRef
Increments the ref count by one and returns the resultant ref count.
ULONG InternalAddRef ()
InternalRelease
Decrements the ref count by one and returns the resultant ref count.
ULONG InternalRelease ()
9.11.2 CComClassFactory
Derived from IClassFactory and CComObjectRoot.
Constructor
AddRef
Increments the ref count by one and returns the resultant ref count. This is handled
by a call to InternalAddRef( ) in CComObjectRoot.
ULONG STDMETHODCALLTYPE AddRef ()
386
9
VxDCOM Applications
Release
Decrements the ref count by one and returns the resultant ref count. This is
handled by a call to InternalRelease( ) in CComObjectRoot.
ULONG STDMETHODCALLTYPE Release ()
QueryInterface
CreateInstance
LockServer
This is a stub function, which always returns S_OK and is provided for
compatibility only.
HRESULT STDMETHODCALLTYPE LockServer
(
BOOL Lock
)
9.11.3 CComCoClass
VxDCOM does not implement the macros DECLARE_CLASSFACTORY or
DECLARE_AGGREGATABLE. The functionality is implicitly built into the class.
387
VxWorks 5.5
Programmers Guide
GetObjectCLSID
9.11.4 CComObject
Derived from the given interface class.
CreateInstance
There are two versions of CreateInstance. For details about when each is used and
how it is invoked, see the Microsoft COM documentation.
CreateInstance as defined below creates a single instance with no aggregation and
no specific COM interface.
static HRESULT CreateInstance
(
CComObject** pp // object that is created
)
The version of CreateInstance below creates an instance of the class and searches
for the requested interface on it.
static HRESULT CreateInstance
(
IUnknown* punkOuter, // aggretable interface
REFIID riid, // GUID of the interface
void** ppv // resultant object
)
AddRef
Release
388
9
VxDCOM Applications
QueryInterface
9.11.5 CComPtr
Template class that takes a COM interface specifying the type of pointer to be
stored.
Constructors
Supported constructors.
CComPtr ()
CComPtr (Itf* p)
CComPtr (const CComPtr& sp)
Release
Operators
Supported operators.
operator Itf* () const
Itf** operator& ()
Itf* operator-> ()
const Itf* operator-> () const
Itf* operator= (Itf* p)
Itf* operator= (const CComPtr& sp)
bool operator! () const
Attach
389
VxWorks 5.5
Programmers Guide
Detach
Detach an object from the pointer without decrementing its ref count.
Itf *Detach()
CopyTo
9.11.6 CComBSTR
Class wrapper for the BSTR type. CComBSTR provides methods for the safe
creation, assignment, conversion and destruction of a BSTR.
Constructors
Supported constructors.
CComBSTR
explicit
explicit
explicit
()
CComBSTR (int nSize, LPCOLESTR sz = 0)
CComBSTR (LPCOLESTR psz)
CComBSTR (const CComBSTR& src)
Operators
Supported operators.
CComBSTR& operator= (const CComBSTR& cbs)
CComBSTR& operator= (LPCOLESTR pSrc)
operator BSTR () const
BSTR * operator& ()
bool operator! () const
CComBSTR& operator+= (const CComBSTR& cbs)
Length
390
9
VxDCOM Applications
Copy
Make a copy of the BSTR within the wrapper class and return it.
BSTR Copy() const
Append
Empty
Attach
Detach
Detach the BSTR from the wrapper class and return it.
BSTR Detach ()
9.11.7 VxComBSTR
The comObjLibExt file provides VxWorks specific extensions to the existing
ATL-like classes defined in comObjLib.h. VxComBSTR is derived from
CComBSTR and extends it.
Constructors
Constructors for this class are derived from the CComBSTR constructors.
VxComBSTR ()
explicit VxComBSTR (int nSize, LPCOLESTR sz = 0)
explicit VxComBSTR (const char * pstr)
explicit VxComBSTR (LPCOLESTR psz)
391
VxWorks 5.5
Programmers Guide
Operators
Return the decimal numeric value stored in the BSTR as a DWORD value. This
method follows the same string rules as the standard library function atoi:
operator DWORD ()
Convert a DWORD value into its decimal string representation and store it as a
BSTR:
VxComBSTR& operator = (const DWORD& src)
Convert a DOUBLE value into it's decimal string representation and stores it as a
BSTR:
VxComBSTR& operator = (const DOUBLE& src)
Convert an array of char into a BSTR format. It can be used instead of T2OLE:
VxComBSTR& operator = (const char * src)
Return TRUE if the given VxComBSTR value is equal to the stored BSTR, FALSE
otherwise:
bool const operator == (const VxComBSTR& src)
Return TRUE if the given VxComBSTR value is not equal to the stored BSTR,
FALSE otherwise:
bool const operator != (const VxComBSTR& src)
SetHex
Convert a DWORD value into its hexadecimal string representation and stored it
as a BSTR.
void SetHex (const DWORD src)
392
9
VxDCOM Applications
9.11.8 CComVariant
Derived from tagVARIANT.
Constructors
Supported Constructors.
CComVariant()
CComVariant(const VARIANT& varSrc)
CComVariant(const CComVariant& varSrc)
CComVariant(BSTR bstrSrc)
CComVariant(LPCOLESTR lpszSrc)
CComVariant(bool bSrc)
CComVariant(int nSrc)
CComVariant(BYTE nSrc)
CComVariant(short nSrc)
CComVariant(long nSrc, VARTYPE vtSrc = VT_I4)
CComVariant(float fltSrc)
CComVariant(double dblSrc)
CComVariant(CY cySrc)
CComVariant(IUnknown* pSrc)
Operators
Supported operators.
CComVariant& operator=(const CComVariant& varSrc)
CComVariant& operator=(const VARIANT& varSrc)
CComVariant& operator=(BSTR bstrSrc)
CComVariant& operator=(LPCOLESTR lpszSrc)
CComVariant& operator=(bool bSrc)
CComVariant& operator=(int nSrc)
CComVariant& operator=(BYTE nSrc)
CComVariant& operator=(short nSrc)
CComVariant& operator=(long nSrc)
CComVariant& operator=(float fltSrc)
CComVariant& operator=(double dblSrc)
CComVariant& operator=(CY cySrc)
CComVariant& operator=(IUnknown* pSrc)
bool operator==(const VARIANT& varSrc)
bool operator!=(const VARIANT& varSrc)
Clear
393
VxWorks 5.5
Programmers Guide
Copy
Attach
Detach
ChangeType
Change the type of the VARIANT to vtNew. The types supported by this wrapper
function are the same as those of the comLib function VariantChangeType.
HRESULT ChangeType
(
VARTYPE vtNew,
const VARIANT* pSrc = NULL
)
394
10
Distributed Message Queues
Optional Component VxFusion
10.1 Introduction
VxFusion is a lightweight, media-independent mechanism, based on VxWorks
message queues, for developing distributed applications.
There are several options for distributed multiprocessing in VxWorks. The Wind
River optional product VxMP allows objects to be shared, but only across shared
memory. TCP/IP can be used to communicate across networks, but it is low level
and not intended for real-time use. Various high-level communication
mechanisms that are standard for distributed computing can be used, but they
have high overheads in terms of memory usage and computation time that are not
always acceptable for real-time systems. Numerous proprietary methods also have
been developed, but more and more often they encounter maintenance, porting,
and enhancement issues. VxFusion, however, is a standard VxWorks component
that:
395
VxWorks 5.5
Programmers Guide
Exhibits location transparency; that is, objects can be moved seamlessly within
the system without rewriting application code. Specifically, posting messages
to objects occurs without regard to their location in a multi-node system.
VxFusion is similar to the VxMP shared memory objects option. VxMP adds to the
basic VxWorks message queue functionality support for sharing message queues,
semaphores, and memory allocation over shared memory. VxFusion adds to
VxWorks support for sharing message queues over any transport, as well as
multicasting to message queue groups. Unlike the VxMP shared memory option,
VxFusion does not support distributed semaphores or distributed memory
allocation.
!
broadcast to the other host through the name database. If the host where the queue
was created crashes, there is no easy way for the other host to find out this
information. Thus, the other host might be pending on a receive forever. It is up to
the user to provide ways to detect remote nodes crashes and update the database
accordingly.
396
10
Distributed Message Queues
397
10
VxWorks 5.5
Programmers Guide
Node A
Node B
Application A
Application B
VxFusion
VxFusion
UDP Adapter
UDP Adapter
UDP Transport
VxWorks OS
VxWorks OS
Figure 10-1
UDP Transport
Ethernet LAN
Figure 10-1 illustrates an example two-node VxFusion system. VxFusion has been
installed on each of the two nodes that are connected to the same subnet of an
Ethernet LAN. Because the two nodes are connected by Ethernet, TCP, UDP, IP,
and raw Ethernet are all possible transports for communications.
In Figure 10-1, the UDP protocol serves as the transport and the supplied UDP
adapter as the adapter.
Services and Databases
398
10
Distributed Message Queues
Figure 10-2
VxFusion Components
VxFusion
Services
Table 10-1
Databases
VxFusion Databases
10
Database
Description
The distributed node database maintains the list of all other nodes in the
system along with their status.
Table 10-2
VxFusion Services
Service
Description
Incorporation Service
399
VxWorks 5.5
Programmers Guide
Libraries
Identifies the other VxFusion nodes in the system and determines their status.
If there are other nodes in the system, updates the local databases using data
from one of the other nodes.
400
10
Distributed Message Queues
Parameter/Definition
Default Value
Description
myNodeId
IP address of
booting
interface
distUdpInit( )
booting
interface
9 (512 log 2)
node ID
ifInitRtn
adapter-specific
initialization routine
pIfInitConf
adapter-specific
configuration structure
maxTBufsLog2
maximum number of
TBufs
401
10
VxWorks 5.5
Programmers Guide
Table 10-3
Parameter/Definition
Default Value
Description
maxNodesLog2
5 (32 log 2)
7 (128 log 2)
6 (64 log 2)
8 (256 log 2)
240*
Specify the number of clock ticks to wait for responses from other
nodes at startup.
maximum number of
nodes in the distributed
node database
maxQueuesLog2
maximum number of
queues on node
maxGroupsLog2
maximum number of
groups in the distributed
group database
maxNamesLog2
maximum number of
entries in the distributed
name database
waitNTicks
maximum number of
clock ticks to wait
* 4*sysClkRateGet( ) is typically 240, but not always. The defaults are defined in vxfusion/distLib.h and
can be changed by the user, if desired.
NOTE: If distInit( ) fails, it returns ERROR, and VxFusion is not started on the host.
Customizing with distCtl( )
402
10
Distributed Message Queues
Table 10-4
Parameter or Hook
Default
Value
DIST_CTL_LOG_HOOK
NULL
DIST_CTL_PANIC_HOOK
NULL
DIST_CTL_RETRY_TIMEOUT
200ms
DIST_CTL_MAX_RETRIES
Description
DIST_CTL_NACK_SUPPORT
TRUE
DIST_CTL_PGGYBAK_UNICST_SUPPORT
FALSE
DIST_CTL_PGGYBAK_BRDCST_SUPPORT
FALSE
DIST_CTL_OPERATIONAL_HOOK
NULL
DIST_CTL_CRASHED_HOOK
NULL
DIST_CTL_SERVICE_HOOK
NULL
DIST_CTL_SERVICE_CONF
servicespecific
403
10
VxWorks 5.5
Programmers Guide
Table 10-5 lists configurable fields of the DIST_IF structure, which is used to pass
data about the adapter to VxFusion. It is the DIST_IF structure that gives VxFusion
transport independence. For more information on the DIST_IF structure, see
10.7.2 Writing an Initialization Routine, p.425.
Table 10-5
DIST_IF Field/
Default Value
Description
distIfName
adapter-specific
distIfMTU
adapter-specific
MTU size
distIfHdrSize
adapter-specific
distIfBroadcastAddr
adapter-specific
broadcast address
distIfRngBufSz
adapter-specific
distIfMaxFrags
adapter-specific
maximum number of
fragments
UDP adapter: 10
Definition
404
10
Distributed Message Queues
Typically, the task that wants to share a value adds a name-value-type entry into
the distributed name database. When adding the entry to the database, the task
associates the value with a unique, specified name. Tasks on different nodes use
this name to get the associated value.
Consider the example in Figure 10-3, which shows how two tasks on different
nodes share a common distributed message queue ID.
Figure 10-3
Node 1
Node 2
msgQDistCreate( )
Q1
1
t1
distNameAdd( )
t2
t3
t2
t3
distributed
name
database
2
myObj : Q1 ID : T_DIST_MSG_Q
broadcasting
Internally
the creation
of myObj
distributed
name
database
Node 1
Node 2
Q1
t1
msgQDistSend( )
distNameFind( )
myObj : Q1 ID : T_DIST_MSG_Q
distributed
name
database
myObj : Q1 ID : T_DIST_MSG_Q
distributed
name
database
405
10
VxWorks 5.5
Programmers Guide
message queue, it first finds the ID by looking up the name myObj in Node 2s
local copy of the distributed name database.
However, if node 2 does not receive the broadcastbecause, for example, the
network is downthe information on the two nodes does not match, and Node 2
is not aware of Q1. (This is an example of a case in which
DIST_CTL_CRASHED_HOOK could be used with distCtl( ) for notification; see
Table 10-4.)
Table 10-6 lists the distributed name database service routines. The distributed
name database may contain floating point values because they invoke printf( ) to
print them. Any task calling distNameShow( ) should set the VX_FP_TASK task
option set.
Additional information about adding a name to the distributed names database
and about related show routines is provided in this section. For detailed
information about all of these routines, see the entries in the VxWorks API Reference.
Table 10-6
Functionality
distNameAdd( )
distNameFind( )
distNameFindByValueAndType( )
distNameRemove( )
distNameShow( )
distNameFilterShow( )
from network-byte order for the pre-defined types only. Do not call htnol( ) or
ntohl( ) explicitly for values of pre-defined types from the distributed name
database.
406
10
Distributed Message Queues
Decimal Value
Purpose
T_DIST_MSG_Q
T_DIST_NODE
16
node identifier
T_DIST_UINT8
64
T_DIST_UINT16
65
T_DIST_UINT32
66
T_DIST_UINT64
67
T_DIST_FLOAT
68
T_DIST_DOUBLE
69
10
user-defined types
4096
and above
user-defined types
There are two routines for displaying data from the distributed name database:
distNameShow( ) and distNameFilterShow( ).
!
407
VxWorks 5.5
Programmers Guide
dmq-02
dmq-03
dmq-04
dmq-05
gData
gCount
grp1
grp2
value = 0 = 0x0
T_DIST_MSG_Q 0x3ff98b
T_DIST_MSG_Q 0x3ff94b
T_DIST_MSG_Q 0x3ff8db
T_DIST_MSG_Q 0x3ff89b
4096 0x48 0x65 0x6c 0x6c 0x6f 0x00
T_DIST_UINT32 0x2d (45)
T_DIST_MSG_Q 0x3ff9bb
T_DIST_MSG_Q 0x3ff90b
Functionality
msgQDistCreate( )
msgQDistSend( )
msgQDistReceive( )
msgQDistNumMsgs( )
408
10
Distributed Message Queues
Node 2
msgQDistSend( ) or
msgQSend( )
t2
DBs
Node 1
t3
Q1
DBs
t1
msgQDistReceive( )
or msgQReceive( )
t4
DBs
Node 3
409
10
VxWorks 5.5
Programmers Guide
However, before send and receive operations can occur, a task on Node 1 must
have created a distributed message queue. The remote message queue Q1 has been
previously created by task t1 on Node 1 with a call to msgQDistCreate( ). Q1 was
then added to the distributed name database with a call to distNameAdd( ). Tasks
t2 and t4 have also previously obtained the remote message queue ID for Q1 from
the distributed name database using the distNameFind( ) routine. With this data,
task t2 on Node 2 can send a message to Q1, using either the standard
msgQSend( ) routine or the VxFusion-specific msgQDistSend( ) routine.
Similarly, task t4 on Node 3 can receive a message from Q1 using the standard
msgQReceive( ) routine or the VxFusion-specific msgQDistReceive( ) routine.
For detailed information about distributed message queue routines, see the entries
in the VxWorks API Reference.
Sending Limitations
Local sendingthat is, send actions on a single nodeoccurs in the same manner
for distributed message queues as for standard message queues, and, therefore, is
not discussed in greater detail in this manual. However, sending messages to
remote message queues using the msgQDistSend( ) routine can have different
outcomes, depending on the value specified for the timeout arguments.
Figure 10-5 presents three examples of messages being sent to a remote node.
There are two threads of execution: the local node waits for the status of the send
action, and the remote node waits to place the data in the distributed message
queue. Both threads are controlled with timeouts, if msgQDistSend( ) is used.
The timeout on the remote side is the msgQTimeout argument, the number of ticks
to wait at the remote message queue. The local timeout is the overallTimeout
argument, the number of ticks to wait overall, including the transmission time.
In Example A, no timeout occurs before the send action completes and the status
is returned. Thus, the local node receives the OK and knows that the message was
received. In B and C, one of the timeouts expires, the send routine returns ERROR,
and the errno variable is set to indicate a timeout.
In B, the local node is aware of the timeout; however, in C, the local node times out
before the status is received. In this case, the local node does not know whether or
not the send completed. In Example C, the message has been added to the remote
queue, even though the local operation failed, and the two nodes have different
views of the state of the system. To avoid this problem, set overallTimeout to a value
great enough that the status is always received.
Using msgQSend( ) prevents a situation like Example C from occurring because
msgQSend( ) waits forever for a response from the remote side.
410
10
Distributed Message Queues
Figure 10-5
Sending Scenarios
Sender
Receiver
SEND (data)
STATUS_OK
status = OK
errno = 0
timeout
timeout
10
SEND (data)
B
STATUS_TIMEOUT
status = ERROR
errno = S_objLib_OBJ_UNAVAILABLE
or S_objLib_OBJ_TIMEOUT
timeout
timeout
SEND (data)
C
STATUS_OK
timeout
status = ERROR
errno = S_msgQDistLib_OVERALL_TIMEOUT
timeout
For limitations on sending messages to nodes that are unavailable, see Detecting
Absent Receiving Nodes, p.418.
411
VxWorks 5.5
Programmers Guide
Receiving Limitations
As is the case for local sending of messages, local receiving occurs in the same
manner for distributed message queues as for standard message queues.
Figure 10-6
Receiving Scenarios
Tries to receive
RECV_REQ
RECV_RPL
status = OK
errno = 0
timeout
timeout
RECV_REQ
B
STATUS_TIMEOUT
status = ERROR
errno = S_objLib_OBJ_UNAVAILABLE
or S_objLib_OBJ_TIMEOUT
timeout
timeout
RECV_REQ
C
RECV_RPL
timeout
status = ERROR
errno = S_msgQDistLib_OVERALL_TIMEOUT
412
timeout
10
Distributed Message Queues
As with the scenarios in Sending Limitations, p.410, different outcomes can result
when receiving messages from remote message queues using the
msgQDistReceive( ) routine, depending on the value specified for the timeout
arguments. Figure 10-6 presents three examples of messages being received by a
node. There are two threads of execution: the local node waits for data to be
received, and the remote node waits for data to arrive at the distributed message
queue. Both threads are controlled with timeouts.
The timeout on the remote side is specified by the msgQTimeout argument, the
number of ticks to wait at the remote message queue. The local timeout is specified
by the overallTimeout argument, the number of ticks to wait overall, including the
transmission time.
Example A illustrates a successful receive with no timeouts. A request to receive a
message from a message queue is sent to the remote side and the result is returned
before either thread experiences a timeout.
In B, the remote side experiences a timeout, but a status response is returned to the
local node before the overall timeout expires. The receive routine returns an error
and the errno variable is set to indicate a timeout. Both sides know that the receive
failed and have the same view of the state of the remote message queue.
In C, the local node tries to receive a message from the remote node, but the
overallTimeout expires before a response arrives. The local and remote sides end up
with different views of the state of the remote message queue because, although
the message was successfully removed from the queue on the remote side, the local
side thinks the operation failed.
To avoid this problem, set the overallTimeout argument to a value great enough that
the reply from the remote side is always received; or use msgQReceive( ), because
it waits forever for a response from the remote side.
Displaying the Contents of Distributed Message Queues
To display the contents of a distributed message queue, use the standard message
queue routine msgQShow( ).
The following example shows msgQShow( ) output for a local distributed
message queue:
[VxKernel]-> msgQShow 0xffe47f
Message Queue Id
Global unique Id
Type
Home Node
Mapped to
:
:
:
:
:
0xffe47f
0x930b267b:fe
queue
0x930b267b
0xea74d0
413
10
VxWorks 5.5
Programmers Guide
Message Queue Id
Task Queueing
Message Byte Len
Messages Max
Messages Queued
Receivers Blocked
Send Timeouts
Receive Timeouts
value = 0 = 0x0
:
:
:
:
:
:
:
:
0xea74d0
FIFO
1024
100
0
0
0
0
The following example shows output for the same queue but from a different
machine:
[VxKernel]-> msgQShow 0x3ff9b7
Message Queue Id
Global unique Id
Type
Home Node
value = 0 = 0x0
:
:
:
:
0x3ff9b7
0x930b267b:fe
remote queue
0x930b267b
Functionality
msgQDistGrpAdd( )
msgQDistGrpDelete( )
msgQDistGrpShow( )
414
10
Distributed Message Queues
If you want a distributed message queue to belong to more than one group, you
must call msgQDistGrpAdd( ) to assert each additional membership.
Only sending to and displaying group message queues is supported. It is an error
to try to receive from a group message queue or query it for the number of
messages.
NOTE: Although there is a msgQDistGrpDelete( ) routine, a distributed message
queue cannot be deleted from a group message queue. The msgQDistGrpDelete( )
command will always return ERROR.
Information about all the group message queues that have been created in the
system and their locally added members is stored in the local copy of the
distributed group database. The msgQDistGrpShow( ) routine displays either all
of the groups in the distributed group database along with their locally added
members or a specific group and its locally added members. For more information
on using msgQDistGrpShow( ), see Displaying Information About Distributed Group
Message Queues, p.416.
Figure 10-7
Node 2
msgQSend( ) or msgQDistSend( )
Q2
t2
DBs
Q3
Q2
Q4
t1
DBs
Q2
Q1
t3
DBs
Node 1
Q5
t4
Node 3
Q2
t5
DBs
Q6
Node 4
415
10
VxWorks 5.5
Programmers Guide
Consider the example in Figure 10-7. The distributed message queues Q1, Q3, Q4,
Q5, and Q6 have all been previously created by calls to msgQDistCreate( ). They
have been added to the distributed name database by calls to distNameAdd( ).
The same tasks that created the message queues also added Q3, Q4, and Q5 to the
group message queue Q2 by calling msgQDistGrpAdd( ) for each new member.
Message queues Q1 and Q6 have not been added to the Q2 group. The first call to
msgQDistGrpAdd( ) creates Q2 as an entry in each nodes distributed group
database. (In Figure 10-7, the three databasesdistributed node, name, and
groupare symbolized by three cylinders, the front one of which represents the
group database and shows the Q2 entry.)
NOTE: The distributed message queue members of Q2 did not have to be added
to the group by the task that created them. In fact, any task can add any distributed
message queue to a group, as long as the ID for that queue is known locally or is
available from the distributed name database.
In Figure 10-7, with group message queue Q2 established, when task t1 sends a
message to the group Q2, the message is sent to all nodes in the system. Each node
uses the distributed group database to identify group members and forwards the
message to them. In this example, the message is sent to members Q3, Q4, and Q5,
but not to non-members Q1 and Q6.
For detailed information about distributed group message queues, see the related
entries in the VxWorks API Reference.
Displaying Information About Distributed Group Message Queues
The msgQDistGrpShow( ) routine displays either all of the groups in the group
database along with their locally added members or a specific group and its locally
added members.
The following output demonstrates the use of msgQDistGrpShow( ) with no
arguments:
[VxKernel]-> msgQDistGrpShow(0)
NAME OF GROUP
GROUP ID
STATE MEMBER ID TYPE OF MEMBER
------------------- ---------- ------- ---------- --------------------------grp1
0x3ff9e3 global
0x3ff98b distributed msg queue
0x3ff9fb distributed msg queue
grp2
0x3ff933 global
0x3ff89b distributed msg queue
0x3ff8db distributed msg queue
0x3ff94b distributed msg queue
value = 0 = 0x0
416
10
Distributed Message Queues
The following call demonstrates the use of msgQDistGrpShow( ) with the string
grp1 as the argument:
[VxKernel]-> msgQDistGrpShow("grp1")
NAME OF GROUP
GROUP ID
STATE MEMBER ID TYPE OF MEMBER
------------------- ---------- ------- ---------- --------------------------grp1
0x3ff9e3 global
0x3ff98b distributed msg queue
0x3ff9fb distributed msg queue
value = 0 = 0x0
Adapter Routines
Routine
Functionality
distIfShow( )
For information on how to write your own VxFusion adapters, see 10.7 Designing
Adapters, p.423. For detailed information about adapters, see the related entries in
the VxWorks API Reference.
The following example demonstrates the use of distIfShow( ):
[VxKernel]-> distIfShow
Interface Name
MTU
Network Header Size
SWP Buffer
Maximum Number of Fragments
Maximum Length of Packet
Broadcast Address
Telegrams received
Telegrams received for sending
Incoming Telegrams discarded
Outgoing Telegrams discarded
:
:
:
:
:
:
:
:
:
:
:
"UDP adapter"
1500
14
32
10
14860
0x930b26ff
23
62
0
0
To learn how to change the installed interface adapter or to modify its values, see
10.3.3 Configuring VxFusion, p.401.
417
10
VxWorks 5.5
Programmers Guide
When a distributed message queue is created on one node, the other nodes are
informed of its creation. However, if the node on which the message queue was
created either crashes or is rebooted, there is no simple way for other nodes to
detect the loss of the message queue. The entry in the name database is not
modified, even if the system recreates the queue when it reboots. As a
consequence, the other nodes might use an invalid message queue ID and pend on
a receive notification that will never be sent. It is, therefore, the responsibility of the
application on the receiving node to set timeouts when more data is expected.
Activities
Booting
Network
Operational
418
10
Distributed Message Queues
Table 10-11 lists its three states, each of which is described in detail in this section.
Figure 10-8 illustrates the node startup process. (To simplify Figure 10-8, it does
not show the sending of acknowledgments.)
Figure 10-8
Node 1
Node New
Node 2
start timer
BOOTSTRAP_REQ
BOOTSTRAP_REQ
Booting State
Identify other
VxFusion nodes
in the system,
if any.
BOOTSTRAP_ACK
Identify the
godfather.
BOOTSTRAP_ACK
timeout
INCO_REQ
GRP_DB_ADD
Network State
Update
VxFusion
databases
from the
godfather.
NAME_DB_ADD
INCO_DONE
INCO_UPNOW
INCO_UPNOW
Operational State
The system is
up now.
419
10
VxWorks 5.5
Programmers Guide
Booting State
Once a godfather is located, the local node asks the godfather to update its
databases by sending an incorporation request message (INCO_REQ). The
godfather updates the local node's name and group databases. These updates are
indicated by the GRP_DB_ADD and NAME_DB_ADD arrows in Figure 10-8. The
godfather tells the receiving node that it is finished updating the databases by
sending an incorporation done message (INCO_DONE).
Once the database updates have completed, the node moves into the operational
state. If there is no godfather, the node moves directly from the booting state to the
operational state.
Operational State
When a node moves into the operational state, VxFusion is fully initialized and
running on it. The node broadcasts the up now incorporation message
(INCO_UPNOW) to tell the other nodes in the system that it is now active.
420
10
Distributed Message Queues
A Telegram
Protocol Header
Network Header
Service Header
Service Data
MTU-1
Protocol Header
The transport defines the protocol header, which it builds from values provided by
the adapter. The contents of this header vary from protocol to protocol, but may
include fields such as source address, destination address, and priority, if the
transport supports priority.
Network Header
The adapter defines and builds the network header. See 10.7.1 Designing the
Network Header, p.424 for a detailed description of the network header and its
fields.
Service Header
VxFusion defines and builds the service header. The service header is a small header
that identifies the internal service sending the message and the message type.
421
10
VxWorks 5.5
Programmers Guide
Service Data
The service data is the data being sent. When sending a message to a remote
message queue or a message queue group, this data can be either the entire
message or a fragment of the message to be sent. It is a fragment of the message if
the message size exceeds the space allocated in the telegram for service data.
Because large numbers of telegrams can be necessary when working with
transports having small MTU sizes, VxFusion does not acknowledge individual
telegrams. Instead, only whole messages are acknowledged. In the event of a
transmission error or if a telegram is lost, the whole message must be
re-transmitted.
A Telegram Buffer
DIST_TBUF struct:
.
.
.
pTBufData
Network Header
Service Header
Service Data
tBufSize - 1
422
10
Distributed Message Queues
The DIST_TBUF structure member, pTBufData, contains the address of the start of
the service header. To access the network header, subtract the size of the network
header from this address.
In order to reconstruct a telegram buffer at the remote node, some telegram buffer
fields must be copied into the network header by the adapter before sending the
telegram. For a list of the fields that must be copied into the network header, see
10.7.1 Designing the Network Header, p.424.
This section describes how to write adapters for the VxFusion component.
An adapter is a software mechanism that facilitates communication between
VxFusion nodes. An adapter sits between VxFusion and the single
communications transport, as shown in Figure 10-1. A transport can be a
high-level communications protocol, such as UDP, or a low-level communications
or bus driver, such as an Ethernet driver. When a message is sent to a remote node,
VxFusion passes a telegram buffer to the adapter, and the adapter sends data to the
remote node by way of the supported transport. Similarly, when a message is
received from a remote node, an adapter reconstructs a telegram buffer from the
incoming data and sends the buffer to VxFusion.
An adapter must provide the following:
transport.
423
VxWorks 5.5
Programmers Guide
You must create fields in the adapter-specific network header that correspond to
the following DIST_TBUF fields. All but the first two fields, tBufGen and pTBufData,
are required to reconstruct the telegram buffer on the remote side:
You may need to create and add fields to the network header depending on the
transport. For example, the message priority is an argument to both the
distIfXxxSend( ) and distNetInput( ) routines, but not all transports support
priority. If the transport supports priority, the priority is transmitted in the protocol
424
10
Distributed Message Queues
header and is available at the remote side. If the transport does not support priority
and you want it preserved, then you should add a field to the network header to
transmit the priority value to the remote node.
10
myNodeId,
ifInitRtn,
*pIfInitConf,
maxTBufsLog2,
maxNodesLog2,
maxQueuesLog2,
maxGroupsLog2,
maxNamesLog2,
waitNTicks
/*
/*
/*
/*
/*
/*
/*
/*
/*
You can base your version of distIfXxxInit( ) on the following prototypical code:
STATUS distIfXxxInit
(
void
*pConf,
FUNCPTR *pStartup
);
425
VxWorks 5.5
Programmers Guide
pStartup
a pointer to a startup routine that is set up after the adapter initialization
routine returns
The adapter initialization routine should perform the following operations:
set the startup routine pointer to point to the adapter startup routine
The DIST_IF structure is one of the mechanisms that provides VxFusion with its
transport independence: the structure maintains its form regardless of the adapter
being used. The DIST_IF structure is composed of configurable fields that identify
the adapter to be used and operating conditions for sending messages. For
information about using DIST_IF, see Using the DIST_IF Structure, p.426.
Although you cannot call distInit( ) and distIfXxxInit( ) directly, you can modify
the VxFusion startup code in usrVxFusion.c to change the VxFusion initialization
process.
If you need to specify additional information to initialize the adapter, you can
modify the pIfInitConf argument of distInit( ) to provide that information. The
pIfInitConf argument is passed as the pConf argument of distIfXxxInit( ). To
preserve information pointed to by pConf, you should copy its values into a more
permanent structure within the adapter. If the adapter needs no additional
configuration information, then pConf should be ignored.
The adapter initialization routine should return OK if initialization is successful, or
ERROR if it fails.
Use the DIST_IF structure to pass information about the adapter to VxFusion so
VxFusion can fragment messages into telegrams of appropriate size to be sent out
over the transport.
The DIST_IF structure has the following declaration:
typedef struct
{
char
int
int
DIST_NODE_ID
short
short
426
/* DIST_IF */
*distIfName;
distIfMTU;
distIfHdrSize;
distIfBroadcastAddr;
distIfRngBufSz;
distIfMaxFrags;
/*
/*
/*
/*
/*
/*
10
Distributed Message Queues
int
STATUS
427
10
VxWorks 5.5
Programmers Guide
The startup routine is invoked by distInit( ) after the network layer of VxFusion is
initialized. The startup routine should spawn the input task, as well as any
initialization or startup that must be done to enable the transmission and reception
of telegrams over the desired transport. For example, at this time, a UDP adapter
should create the socket that it uses for communications.
The startup routine should return OK if the operation is successful, or ERROR if it
fails.
/* destination node */
/* TBUF to send */
/* packet priority */
428
10
Distributed Message Queues
Locate and fill in the pre-allocated network header with values from the
telegram buffer that is passed in as an argument. The network header fields
should be filled in using network byte order, so they can be correctly decoded
on the remote side, even if the remote node uses a different byte order. (For
information about which telegram buffer fields must be copied, see
10.7.1 Designing the Network Header, p.424.)
Fill in any additional network header fields (such as priority) that may need to
be filled in.
The purpose of the input routine is to read a telegram and send it to VxFusion by
calling distNetInput( ). For more information, see the entry for distNetInput( ) in
the VxWorks API Reference.
The input routine should listen or wait for an incoming telegram from the
transport. Upon receipt of the telegram, the statistics field distStat.ifInReceived
should be incremented. Then, the telegram should be tested to make sure that it is
longer than the network header. If not, the telegram is too small and should be
ignored. The distStat.ifInLength and distStat.ifInDiscarded fields should also be
incremented, in this case.
If your transport does not discard broadcast packets sent from itself, use the input
routine to filter out transport-originated broadcast packets.
After the input routine has discarded any duplicate or faulty telegram that
originates from the transport, the incoming telegram is assumed to be correct
(although there is one more check made later). A telegram buffer is allocated and
the contents of the telegram are copied into it. The non-data fields of the telegram
buffer that were not transmitted are as follows:
429
10
VxWorks 5.5
Programmers Guide
tBufId
tBufAck
tBufSeq
tBufNBytes
tBufType
tBufFlags
These fields are reconstructed using the network header. During the
reconstruction, these fields should be converted back to host order from network
order.
After the telegram buffer is reconstructed and the number of bytes expected in the
non-header portion of the telegram are known (from the tBufNBytes field),
telegram length is compared to tBufNBytes plus the size of the network header. If
the lengths do not match, the telegram should be discarded and the statistics
distStat.ifInLength and distStat.ifInDiscarded incremented.
If the lengths match, the telegram should be forwarded upward by calling
distNetInput( ).
You determine the control functions that must be supported by the adapter.
This routine should return the return value of the I/O control function being
performed if successful, or ERROR if the operation fails. If no control functions are
provided, the distIfXxxIoctl( ) routine should return ERROR.
430
11
Shared-Memory Objects
Optional Component VxMP
11.1 Introduction
VxMP is an optional VxWorks component that provides shared-memory objects
dedicated to high-speed synchronization and communication between tasks
running on separate CPUs. For information on how to install VxMP, see Tornado
Getting Started.
Shared-memory objects are a class of system objects that can be accessed by tasks
running on different processors. They are called shared-memory objects because the
objects data structures must reside in memory accessible by all processors.
Shared-memory objects are an extension of local VxWorks objects. Local objects are
only available to tasks on a single processor. VxMP supplies three kinds of sharedmemory objects:
431
VxWorks 5.5
Programmers Guide
432
11
Shared-Memory Objects
Functionality
smNameAdd( )
smNameRemove( )
smNameFind( )
smNameFindByValue( )
smNameShow( )
433
11
VxWorks 5.5
Programmers Guide
Hex Value
T_SM_SEM_B
T_SM_SEM_C
T_SM_MSG_Q
T_SM_PART_ID
T_SM_BLOCK
The output is sent to the standard output device, and looks like the following:
Name in Database Max : 100 Current : 5 Free : 95
Name
Value
Type
----------------- ------------- ------------myMemory
0x3835a0
SM_BLOCK
myMemPart
0x3659f9
SM_PART_ID
434
11
Shared-Memory Objects
myBuff
mySmSemaphore
myMsgQ
0x383564
0x36431d
0x365899
SM_BLOCK
SM_SEM_B
SM_MSG_Q
435
11
VxWorks 5.5
Programmers Guide
waiting for t1 to indicate that the object is ready for t2. When t1 is ready to transfer
control of the object to t2, it gives the semaphore, readying t2 on CPU1.
Table 11-3
Description
semBSmCreate( )
semCSmCreate( )
There are two types of shared semaphores, binary and counting. Shared
semaphores have their own create routines and return a SEM_ID. Table 11-3 lists
the create routines. All other semaphore routines, except semDelete( ), operate
transparently on the created shared semaphore.
Figure 11-1
Pended Queue
task2
Semaphore
State
EMPTY
task1
Executes on CPU 1
before task2:
task1 ( )
{
...
semTake (semSmId,t);
...
}
SHARED MEMORY
The use of shared semaphores and local semaphores differs in several ways:
436
The shared semaphore queuing order specified when the semaphore is created
must be FIFO. Figure 11-1 shows two tasks executing on different CPUs, both
trying to take the same semaphore. Task 1 executes first, and is put at the front
11
Shared-Memory Objects
Use semInfo( ) to get the shared task control block of tasks pended on a shared
semaphore. Use semShow( ), if INCLUDE_SEM_SHOW is included in the project
facility VxWorks view, to display the status of the shared semaphore and a list of
pended tasks. The following example displays detailed information on the shared
semaphore mySmSemaphoreId as indicated by the second argument (0 =
summary, 1 = details):
-> semShow mySmSemaphoreId, 1
value = 0 = 0x0
11
The output is sent to the standard output device, and looks like the following:
Semaphore Id
: 0x36431d
Semaphore Type : SHARED BINARY
Task Queuing
: FIFO
Pended Tasks
: 2
State
: EMPTY
TID
CPU Number
Shared TCB
------------- ------------- -------------0xd0618
1
0x364204
0x3be924
0
0x36421c
Example 11-1
Shared Semaphores
The following code example depicts two tasks executing on different CPUs and
using shared semaphores. The routine semTask1( ) creates the shared semaphore,
initializing the state to full. It adds the semaphore to the name database (to enable
the task on the other CPU to access it), takes the semaphore, does some processing,
and gives the semaphore. The routine semTask2( ) gets the semaphore ID from the
database, takes the semaphore, does some processing, and gives the semaphore.
/* semExample.h - shared semaphore example header file */
#define SEM_NAME "mySmSemaphore"
/* semTask1.c - shared semaphore example */
/* This code is executed by a task on CPU #1 */
#include "vxWorks.h"
#include "semLib.h"
#include "semSmLib.h"
437
VxWorks 5.5
Programmers Guide
#include
#include
#include
#include
"smNameLib.h"
"stdio.h"
"taskLib.h"
"semExample.h"
/*
* semTask1 - shared semaphore user
*/
STATUS semTask1 (void)
{
SEM_ID semSmId;
/* create shared semaphore */
if ((semSmId = semBSmCreate (SEM_Q_FIFO, SEM_FULL)) == NULL)
return (ERROR);
/* add object to name database */
if (smNameAdd (SEM_NAME, semSmId, T_SM_SEM_B) == ERROR)
return (ERROR);
/* grab shared semaphore and hold it for awhile */
semTake (semSmId, WAIT_FOREVER);
/* normally do something useful */
printf ("Task1 has the shared semaphore\n");
taskDelay (sysClkRateGet () * 5);
printf ("Task1 is releasing the shared semaphore\n");
/* release shared semaphore */
semGive (semSmId);
return (OK);
}
/* semTask2.c - shared semaphore example */
/* This code is executed by a task on CPU #2. */
#include "vxWorks.h"
#include "semLib.h"
#include "semSmLib.h"
#include "smNameLib.h"
#include "stdio.h"
#include "semExample.h"
/*
* semTask2 - shared semaphore user
*/
438
11
Shared-Memory Objects
11
semGive (semSmId);
printf ("Task2 has released the shared semaphore\n");
return (OK);
}
439
VxWorks 5.5
Programmers Guide
request from the client, it finds in the message the ID of the queue to use when
replying to that client. Task t1 then sends the reply to the client by using this ID.
To pass messages between tasks on different CPUs, first create the message queue
by calling msgQSmCreate( ). This routine returns a MSG_Q_ID. This ID is used for
sending and receiving messages on the shared message queue.
Like their local counterparts, shared message queues can send both urgent or
normal priority messages.
Figure 11-2
Pended Queue
task2
Message
Queue
EMPTY
task1
The use of shared message queues and local message queues differs in several
ways:
The shared message queue task queueing order specified when a message
queue is created must be FIFO. Figure 11-2 shows two tasks executing on
different CPUs, both trying to receive a message from the same shared
message queue. Task 1 executes first, and is put at the front of the queue
because there are no messages in the message queue. Task 2 (executing on a
different CPU) tries to receive a message from the message queue after task 1s
attempt and is put on the queue behind task 1.
440
11
Shared-Memory Objects
To achieve optimum performance with shared message queues, align send and
receive buffers on 4-byte boundaries.
To display the status of the shared message queue as well as a list of tasks pended
on the queue, select INCLUDE_MSG_Q_SHOW for inclusion in the project facility
VxWorks view and call msgQShow( ). The following example displays detailed
information on the shared message queue 0x7f8c21 as indicated by the second
argument (0 = summary display, 1 = detailed display).
-> msgQShow 0x7f8c21, 1
value = 0 = 0x0
The output is sent to the standard output device, and looks like the following:
11
Example 11-2
In the following code example, two tasks executing on different CPUs use shared
message queues to pass data to each other. The server task creates the request
message queue, adds it to the name database, and reads a message from the queue.
The client task gets the smRequestQId from the name database, creates a reply
message queue, bundles the ID of the reply queue as part of the message, and
sends the message to the server. The server gets the ID of the reply queue and uses
it to send a message back to the client. This technique requires the use of the
network byte-order conversion macros htonl( ) and ntohl( ), because the numeric
queue ID is passed over the network in a data field.
/* msgExample.h - shared message queue example header file */
#define MAX_MSG
(10)
#define MAX_MSG_LEN (100)
#define REQUEST_Q
"requestQue"
441
VxWorks 5.5
Programmers Guide
"vxWorks.h"
"msgQLib.h"
"msgQSmLib.h"
"stdio.h"
"smNameLib.h"
"msgExample.h"
"netinet/in.h"
442
11
Shared-Memory Objects
"vxWorks.h"
"msgQLib.h"
"msgQSmLib.h"
"smNameLib.h"
"stdio.h"
"msgExample.h"
"netinet/in.h"
/*
* clientTask - sends request to server and reads reply
*/
STATUS clientTask
(
char * pRequestToServer
11
)
{
MSG_Q_ID
smRequestQId; /* request message queue */
MSG_Q_ID smReplyQId;
/* reply message queue */
REQUEST_MSG request;
/* request text */
int
objType;
/* dummy variable for smNameFind */
char
serverReply[MAX_MSG_LEN]; /*buffer for servers reply */
/* get request queue ID using its name */
if (smNameFind (REQUEST_Q, (void **) &smRequestQId, &objType,
WAIT_FOREVER) == ERROR)
return (ERROR);
/* create reply queue, build request and send it to server */
if ((smReplyQId = msgQSmCreate (MAX_MSG, MAX_MSG_LEN,
MSG_Q_FIFO)) == NULL)
return (ERROR);
request.replyQId = (MSG_Q_ID) htonl ((int) smReplyQId);
strcpy (request.clientRequest, pRequestToServer);
if (msgQSend (smRequestQId, (char *) &request, sizeof (REQUEST_MSG),
WAIT_FOREVER, MSG_PRI_NORMAL) == ERROR)
return (ERROR);
443
VxWorks 5.5
Programmers Guide
444
11
Shared-Memory Objects
first time a task must access the shared data structure, it looks up the address of the
memory in the database and gets the semaphore ID from a field in the shared data
structure. Whenever a task must access the shared data, it must first take the
semaphore. Whenever a task is finished with the shared data, it must give the
semaphore.
For example, assume two tasks executing on two different CPUs must share data.
Task t1 executing on CPU 1 allocates a memory block from the shared-memory
system partition and converts the local address to a global address. It then adds the
global address of the shared data to the name database with the name
mySharedData. Task t1 also creates a shared semaphore and stores the ID in the
first field of the data structure residing in the shared memory. Task t2 executing on
CPU 2 looks up the name mySharedData in the name database to get the address
of the shared memory. It then converts this address to a local address. Before
accessing the data in the shared memory, t2 gets the shared semaphore ID from the
first field of the data structure residing in the shared-memory block. It then takes
the semaphore before using the data and gives the semaphore when it is done
using the data.
User-Created Partitions
To make use of user-created shared-memory partitions, a task creates a sharedmemory partition and adds it to the name database. Before a task can use the
shared-memory partition, it must first look in the name database to get the
partition ID. When the task has the partition ID, it can access the memory in the
shared-memory partition.
For example, task t1 creates a shared-memory partition and adds it to the name
database using the name myMemPartition. Task t2 executing on another CPU
wants to allocate memory from the new partition. Task t2 first looks up
myMemPartition in the name database to get the partition ID. It can then allocate
memory from it, using the ID.
The shared-memory system partition is analogous to the system partition for local
memory. Table 11-4 lists routines for manipulating the shared-memory system
partition.
Routines that return a pointer to allocated memory return a local address (that is,
an address suitable for use from the local CPU). To share this memory across
445
11
VxWorks 5.5
Programmers Guide
Table 11-4
Functionality
smMemMalloc( )
smMemCalloc( )
smMemRealloc( )
smMemFree( )
smMemShow( )
smMemOptionsSet( )
smMemAddToPool( )
smMemFindMax( )
The following code example uses memory from the shared-memory system
partition to share data between tasks on different CPUs. The first member of the
data structure is a shared semaphore that is used for mutual exclusion. The send
task creates and initializes the structure, then the receive task accesses the data and
displays it.
/* buffProtocol.h - simple buffer exchange protocol header file */
#define BUFFER_SIZE
#define BUFF_NAME
200
"myMemory"
446
11
Shared-Memory Objects
"vxWorks.h"
"semLib.h"
"semSmLib.h"
"smNameLib.h"
"smObjLib.h"
"stdio.h"
"buffProtocol.h"
/*
* buffSend - write to shared semaphore protected buffer
*/
STATUS buffSend (void)
{
SHARED_BUFF * pSharedBuff;
SEM_ID
mySemSmId;
11
447
VxWorks 5.5
Programmers Guide
"vxWorks.h"
"semLib.h"
"semSmLib.h"
"smNameLib.h"
"smObjLib.h"
"stdio.h"
"buffProtocol.h"
/*
* buffReceive - receive shared semaphore protected buffer
*/
STATUS buffReceive (void)
{
SHARED_BUFF * pSharedBuff;
SEM_ID
mySemSmId;
int
objType;
/* get shared buffer address from name database */
if (smNameFind (BUFF_NAME, (void **) &pSharedBuff,
&objType, WAIT_FOREVER) == ERROR)
return (ERROR);
/* convert global address of buff to its local value */
pSharedBuff = (SHARED_BUFF *) smObjGlobalToLocal (pSharedBuff);
/* convert shared semaphore ID to host (local) byte order */
mySemSmId = (SEM_ID) ntohl ((int) pSharedBuff->semSmId);
/* take shared semaphore before reading the data buffer */
if (semTake (mySemSmId,WAIT_FOREVER) != OK)
return (ERROR);
/* read data buffer and print it */
printf ("Receiver reading from shared memory: %s\n", pSharedBuff->buff);
/* give back the data buffer semaphore */
if (semGive (mySemSmId) != OK)
return (ERROR);
return (OK);
}
448
11
Shared-Memory Objects
User-Created Partition
This example is similar to Example 11-3, which uses the shared-memory system
partition. This example creates a user-defined partition and stores the shared data
in this new partition. A shared semaphore is used to protect the data.
/* memPartExample.h - shared memory partition example header file */
#define
#define
#define
#define
CHUNK_SIZE
MEM_PART_NAME
PART_BUFF_NAME
BUFFER_SIZE
(2400)
"myMemPart"
"myBuff"
(40)
11
"vxWorks.h"
"memLib.h"
"semLib.h"
"semSmLib.h"
"smNameLib.h"
"smObjLib.h"
"smMemLib.h"
"stdio.h"
"memPartExample.h"
/*
* memPartSend - send shared memory partition buffer
*/
STATUS memPartSend
{
char *
PART_ID
SEM_ID
SHARED_BUFF *
(void)
pMem;
smMemPartId;
mySemSmId;
pSharedBuff;
449
VxWorks 5.5
Programmers Guide
450
"vxWorks.h"
"memLib.h"
"stdio.h"
"semLib.h"
11
Shared-Memory Objects
#include "semSmLib.h"
#include "stdio.h"
#include "memPartExample.h"
/*
* memPartReceive - receive shared memory partition buffer
*
* execute on CPU 1 - use a shared semaphore to protect shared memory
*/
STATUS memPartReceive (void)
{
SHARED_BUFF * pBuff;
SEM_ID
mySemSmId;
int
objType;
/* get shared buffer address from name database */
if (smNameFind (PART_BUFF_NAME, (void **) &pBuff, &objType,
WAIT_FOREVER) == ERROR)
return (ERROR);
/* convert global address of buffer to its local value */
pBuff = (SHARED_BUFF *) smObjGlobalToLocal (pBuff);
/* Grab shared semaphore before using the shared memory */
mySemSmId = (SEM_ID) ntohl ((int) pBuff->semSmId);
semTake (mySemSmId,WAIT_FOREVER);
printf ("Receiver reading from shared memory: %s\n", pBuff->buff);
semGive (mySemSmId);
return (OK);
}
Like their local counterparts, shared-memory partitions (both system- and usercreated) can have different options set for error handling; see the reference entries
for memPartOptionsSet( ) and smMemOptionsSet( ).
If the MEM_BLOCK_CHECK option is used in the following situation, the system
can get into a state where the memory partition is no longer available. If a task
attempts to free a bad block and a bus error occurs, the task is suspended. Because
shared semaphores are used internally for mutual exclusion, the suspended task
still has the semaphore, and no other task has access to the memory partition. By
default, shared-memory partitions are created without the MEM_BLOCK_CHECK
option.
451
11
VxWorks 5.5
Programmers Guide
CAUTION: Boards that make use of VxMP must support hardware test-and-set
452
11
Shared-Memory Objects
Operating time for the spin-lock cycle varies greatly because it is affected by the
processor cache, access time to shared memory, and bus traffic. If the lock is not
obtained after the maximum number of tries specified by SM_OBJ_MAX_TRIES
(defined in the Params tab of the properties window for shared memory objects in
the VxWorks view), errno is set to S_smObjLib_LOCK_TIMEOUT. If this error
occurs, set the maximum number of tries to a higher value. Note that any failure
to take a spin-lock prevents proper functioning of shared-memory objects. In most
cases, this is due to problems with the shared-memory configuration; see
11.5.2 Troubleshooting Techniques, p.462.
11.3.4 Restrictions
Unlike local semaphores and message queues, shared-memory objects cannot be
used at interrupt level. No routines that use shared-memory objects can be called
from ISRs. An ISR is dedicated to handle time-critical processing associated with
an external event; therefore, using shared-memory objects at interrupt time is not
appropriate. On a multiprocessor system, run event-related time-critical
processing on the CPU where the time-related interrupt occurred.
Note that shared-memory objects are allocated from dedicated shared-memory
pools, and cannot be deleted.
When using shared-memory objects, the maximum number of each object type
must be specified on the Params tab of the properties window; see 11.4.3 Initializing
the Shared-Memory Objects Package, p.456. If applications are creating more than the
specified maximum number of objects, it is possible to run out of memory. If this
happens, the shared object creation routine returns an error and errno is set to
S_memLib_NOT_ENOUGH_MEM. To solve this problem, first increase the
maximum number of shared-memory objects of corresponding type; see Table 11-5
for a list of the applicable configuration constants. This decreases the size of the
shared-memory system pool because the shared-memory pool uses the remainder
of the shared memory. If this is undesirable, increase both the number of the
corresponding shared-memory objects and the size of the overall shared-memory
453
11
VxWorks 5.5
Programmers Guide
11.4 Configuration
To include shared-memory objects in VxWorks, select INCLUDE_SM_OBJ for
inclusion in the project facility VxWorks view. Most of the configuration is already
done automatically from usrSmObjInit( ) in usrConfig.c. However, you may also
need to modify some values in the Params tab of the properties window to reflect
your configuration; these are described in this section.
454
11
Shared-Memory Objects
the shared-memory network driver. The anchor stores a pointer to the sharedmemory header, a pointer to the shared-memory packet header (used by the
shared-memory network driver), and a pointer to the shared-memory object
header.
The address of the anchor is defined in the Params tab of the Properties window
with the constant SM_ANCHOR_ADRS. If the processor is booted with the sharedmemory network driver, the anchor address is the same value as the boot device
(sm=anchorAddress). The shared-memory object initialization code uses the value
from the boot line instead of the constant. If the shared-memory network driver is
not used, modify the definition of SM_ANCHOR_ADRS as appropriate to reflect
your system.
Two types of interrupts are supported and defined by SM_INT_TYPE: mailbox
interrupts and bus interrupts (see VxWorks Network Programmers Guide: Data Link
Layer Network Components). Mailbox interrupts (SM_INT_MAILBOX) are the
preferred method, and bus interrupts (SM_INT_BUS) are the second choice. If
interrupts cannot be used, a polling scheme can be employed (SM_INT_NONE),
but this is much less efficient.
When a CPU initializes its shared-memory objects, it defines the interrupt type as
well as three interrupt arguments. These describe how the CPU is notified of
events. These values can be obtained for any attached CPU by calling
smCpuInfoGet( ).
The default interrupt method for a target is defined by SM_INT_TYPE,
SM_INT_ARG1, SM_INT_ARG2, and SM_INT_ARG3 on the Params tab.
455
11
VxWorks 5.5
Programmers Guide
Figure 11-3
Shared-Memory Layout
SHARED MEMORY
SM_ANCHOR_ADRS
0x600 (default)
.
.
.
Shared-Memory
Anchor
pointer to shared-memory
objects shared-memory region
~
~
~
~
shared-memory objects
Shared-Memory
Region
CPU 0
CPU 1
sm=0x1800600
RAM
anchor
0x600
allocated
pool
456
Local address of
VMEbus address 0
is 0x1000000
11
Shared-Memory Objects
On the Params tab of the properties window for the master, SM_OFF_BOARD is
FALSE and SM_ANCHOR_ADRS is 0x600. SM_OBJ_MEM_ADRS is set to NONE,
because on-board memory is used (it is malloced at run-time);
SM_OBJ_MEM_SIZE is set to 0x20000. For the slave, the board maps the base of the
VME bus to the address 0x1000000. SM_OFF_BOARD is TRUE and the anchor
address is 0x1800600. This is calculated by taking the VMEbus address (0x800000)
and adding it to the anchor address (0x600). Many boards require further address
translation, depending on where the board maps VME memory. In this example,
the anchor address for the slave is 0x1800600, because the board maps the base of
the VME bus to the address 0x1000000.
Figure 11-5
CPU 0
CPU 1
anchor = 0x3000000
sm=0x2100000
Local address of
VMEbus address 0
is 0x1000000
Local address of
VMEbus address 0
is 0x100000
External RAM
Board (1MB)
anchor
shared-memory
pool
VMEbus address
of RAM on external
board = 0x2000000
457
11
VxWorks 5.5
Programmers Guide
the MMU on, memory that is off-board must be made non-cacheable. This is done
using the data structure sysPhysMemDesc in sysLib.c. This data structure must
contain a virtual-to-physical mapping for the VME address space used for the
shared-memory pool, and mark the memory as non-cacheable. (Most BSPs include
this mapping by default.) See 12.3 Virtual Memory Configuration, p.466, for
additional information.
!
CAUTION: For the MC68K in general, if the MMU is off, data caching must be
Default
Value
SM_OBJ_MAX_TASK
40
SM_OBJ_MAX_SEM
30
SM_OBJ_MAX_NAME
100
SM_OBJ_MAX_MSG_Q
10
SM_OBJ_MAX_MEM_PART
Description
If the size of the objects created exceeds the shared-memory region, an error
message is displayed on CPU 0 during initialization. After shared memory is
configured for the shared objects, the remainder of shared memory is used for the
shared-memory system partition.
458
11
Shared-Memory Objects
Available
--------39
27
27
9
3
95
CAUTION: If the master CPU is rebooted, it is necessary to reboot all the slaves. If
459
11
VxWorks 5.5
Programmers Guide
Table 11-6
Master
(CPU 0)
Slaves
(CPU 1,
CPU 2)
Symbolic Constant
SM_OBJ_MAX_TASK
20
SM_OBJ_MAX_SEM
20
SM_OBJ_MAX_NAME
100
SM_OBJ_MAX_MSG_Q
12
SM_OBJ_MAX_MEM_PART
SM_OFF_BOARD
FALSE
SM_MEM_ADRS
NONE
SM_MEM_SIZE
0x10000
SM_OBJ_MEM_ADRS
NONE
SM_OBJ_MEM_SIZE
0x10000
SM_OBJ_MAX_TASK
20
SM_OBJ_MAX_SEM
20
SM_OBJ_MAX_NAME
100
SM_OBJ_MAX_MSG_Q
12
SM_OBJ_MAX_MEM_PART
SM_OFF_BOARD
SM_ANCHOR_ADRS
SM_MEM_ADRS
460
Value
FALSE
(char *) 0xfb800000
SM_ANCHOR_ADRS
SM_MEM_SIZE
0x10000
SM_OBJ_MEM_ADRS
NONE
SM_OBJ_MEM_SIZE
0x10000
11
Shared-Memory Objects
Setting up the shared-memory objects header and its pointer in the sharedmemory anchor, with smObjSetup( ).
2.
3.
11.5 Troubleshooting
Problems with shared-memory objects can be due to a number of causes. This
section discusses the most common problems and a number of troubleshooting
tools. Often, you can locate the problem by rechecking your hardware and
software configurations.
Be sure the anchor address specified is the address seen by the CPU. This can
be defined with the constant SM_ANCHOR_ADRS in the Params tab of the
properties window or at boot time (sm=) if the target is booted with the
shared-memory network.
461
11
VxWorks 5.5
Programmers Guide
If there is heavy bus traffic relating to shared-memory objects, bus errors can
occur. Avoid this problem by changing the bus arbitration mode or by
changing relative CPU priorities on the bus.
The shared-memory heartbeat can be checked to verify that the master CPU
has initialized shared-memory objects. The shared-memory heartbeat is in the
first 4-byte word of the shared-memory object header. The offset to the header
is in the sixth 4-byte word in the shared-memory anchor. (See VxWorks Network
Programmers Guide: Data Link Layer Network Components.)
Thus, if the shared-memory anchor were located at 0x800000:
[VxWorks Boot]: d
800000: 8765 4321
800010: 0000 0000
800020: 0000 0000
0x800000
0000 0001 0000 0000 0000 002c *.eC!...........,*
0000 0170 0000 0000 0000 0000 *...p............*
0000 0000 0000 0000 0000 0000 *................*
The offset to the shared-memory object header is 0x170. To view the sharedmemory object header display 0x800170:
[VxWorks Boot]: d 0x800170
800170: 0000 0050 0000 0000 0000 0bfc 0000 0350 *...P...........P*
462
11
Shared-Memory Objects
The global variable smIfVerbose, when set to 1 (TRUE), causes sharedmemory interface error messages to print to the console, along with additional
details of shared-memory operations. This variable enables you to get runtime information from the device driver level that would be unavailable at the
debugger level. The default setting for smIfVerbose is 0 (FALSE). That can be
reset programmatically or from the shell.
11
463
VxWorks 5.5
Programmers Guide
464
12
Virtual Memory Interface
Basic Support and Optional Component VxVMI
12.1 Introduction
VxWorks provides two levels of virtual memory support. The basic level is
bundled with VxWorks and provides caching on a per-page basis. The full level is
unbundled, and requires the optional component VxVMI. VxVMI provides write
protection of text segments and the VxWorks exception vector table, and an
architecture-independent interface to the CPUs memory management unit
(MMU). For information on how to install VxVMI, see the Tornado Getting Started
Guide.
This chapter contains the following sections:
One that describes a set of routines for manipulating the MMU. VxVMI
provides low-level routines for interfacing with the MMU in an
architecture-independent manner, allowing you to implement your own
virtual memory systems.
465
VxWorks 5.5
Programmers Guide
Description
INCLUDE_MMU_BASIC
INCLUDE_MMU_FULL
INCLUDE_PROTECT_TEXT
INCLUDE_PROTECT_VEC_TABLE
The appropriate default page size for your processor (4 KB or 8KB) is defined by
VM_PAGE_SIZE in your BSP. If you must change this value for some reason,
redefine VM_PAGE_SIZE in config.h. (See the Tornado Users Guide: Configuration
and Build.)
466
12
Virtual Memory Interface
aligned, and must span complete pages. In other words, the first three fields
(virtual address, physical address, and length) of a PHYS_MEM_DESC structure
must all be even multiples of VM_PAGE_SIZE. Specifying elements of
sysPhysMemDesc that are not page-aligned leads to crashes during VxWorks
initialization.
The following example configuration consists of multiple CPUs using the sharedmemory network. A separate memory board is used for the shared-memory pool.
Because this memory is not already mapped, it must be added to
sysPhysMemDesc for all the boards on the network. The memory starts at
0x4000000 and must be made noncacheable, as shown in the following code
excerpt:
/* shared memory */
{
(void *) 0x4000000,
/* virtual address */
(void *) 0x4000000,
/* physical address */
0x20000,
/* length */
/* initial state mask */
VM_STATE_MASK_VALID | VM_STATE_MASK_WRITABLE |VM_STATE_MASK_CACHEABLE,
/* initial state */
VM_STATE_VALID | VM_STATE_WRITABLE | VM_STATE_CACHEABLE_NOT
}
467
12
VxWorks 5.5
Programmers Guide
For MC680x0 boards, the virtual address must be the same as the physical address.
For other boards, the virtual and physical addresses are the same as a matter of
convention.
468
12
Virtual Memory Interface
Some system objects, such as text segments and semaphores, must be accessible to
all tasks in the system regardless of which virtual memory context is made current.
These objects are made accessible by means of global virtual memory. Global virtual
memory is created by mapping all the physical memory in the system (the
mapping is defined in sysPhysMemDesc) to the identical address in the virtual
memory space. In the default system configuration, this initially gives a one-to-one
relationship between physical memory and global virtual memory; for example,
virtual address 0x5000 maps to physical address 0x5000. On some architectures, it
is possible to use sysPhysMemDesc to set up virtual memory so that the mapping
of virtual-to-physical addresses is not one-to-one; see 12.3 Virtual Memory
Configuration, p.466 for additional information.
Global virtual memory is accessible from all virtual memory contexts.
Modifications made to the global mapping in one virtual memory context appear
in all virtual memory contexts. Before virtual memory contexts are created, add all
global memory with vmGlobalMap( ). Global memory that is added after virtual
memory contexts are created may not be available to existing contexts.
469
VxWorks 5.5
Programmers Guide
Initialization
Page States
Each virtual memory page (typically 8KB) has a state associated with it. A page can
be valid/invalid, writable/nonwritable, or cacheable/noncacheable. See
Table 12-2 for the associated constants.
Table 12-2
State Flags
Constant
Description
VM_STATE_VALID
Valid translation
VM_STATE_VALID_NOT
Invalid translation
VM_STATE_WRITABLE
Writable memory
VM_STATE_WRITABLE_NOT
Read-only memory
VM_STATE_CACHEABLE
Cacheable memory
VM_STATE_CACHEABLE_NOT
Noncacheable memory
Validity
A valid state indicates the virtual-to-physical translation is true. When the
translation tables are initialized, global virtual memory is marked as valid.
All other virtual memory is initialized as invalid.
Writability
Pages can be made read-only by setting the state to nonwritable. This is
used by VxWorks to write-protect all text segments.
470
12
Virtual Memory Interface
Cacheability
The caching of memory pages can be prevented by setting the state flags
to noncacheable. This is useful for memory that is shared between
processors (including DMA devices).
Change the state of a page with the routine vmStateSet( ). In addition to specifying
the state flags, a state mask must describe which flags are being changed; see
Table 12-3. Additional architecture-dependent states are specified in vmLib.h.
Table 12-3
State Masks
Constant
Description
VM_STATE_MASK_VALID
VM_STATE_MASK_WRITABLE
VM_STATE_MASK_CACHEABLE
471
VxWorks 5.5
Programmers Guide
Figure 12-1
TRANSLATION
TABLE
TRANSLATION
TABLE
...
GLOBAL
MAPPING
GLOBAL
MAPPING
Default
Virtual Memory Context
...
PRIVATE
MAPPING
Private
Virtual Memory Context
When physical pages are mapped into new sections of the virtual space, the
physical page is accessible from two different virtual addresses (a condition
known as aliasing): the newly mapped virtual address and the virtual address
equal to the physical address in the global virtual memory. This can cause
problems for some architectures, because the cache may hold two different values
for the same underlying memory location. To avoid this, invalidate the virtual
page (using vmStateSet( )) in the global virtual memory. This also ensures that the
data is accessible only when the virtual memory context containing the new
mapping is current.
Figure 12-2 depicts two private virtual memory contexts. The new context (pvmc2)
maps virtual address 0x6000000 to physical address 0x10000. To prevent access to
this address from outside of this virtual context (pvmc1), the corresponding
physical address (0x10000) must be set to invalid. If access to the memory is made
using address 0x10000, a bus error occurs because that address is now invalid.
472
12
Virtual Memory Interface
Figure 12-2
Private
Virtual Memory Context
New
Virtual Memory Context
pvmc1
pvmc2
...
...
0x6000000
0x10000
valid
mapping
...
Private
Virtual
Memory
V
0x10000 0x10000
I
V
V
...
VIRTUAL
ADDRESS
Example 12-1
...
invalid
mapping
Global
Virtual
Memory
PHYSICAL STATE
ADDRESS
...
VIRTUAL
ADDRESS
PHYSICAL STATE
ADDRESS
In the following code example, private virtual memory contexts are used for
allocating memory from a tasks private memory partition. The setup routine,
contextSetup( ), creates a private virtual memory context that is made current
during a context switch. The virtual memory context is stored in the field spare1 in
the tasks TCB. Switch hooks are used to save the old context and install the tasks
private context. Note that the use of switch hooks increases the context switch
time. A user-defined memory partition is created using the private virtual memory
context. The partition ID is stored in spare2 in the tasks TCB. Any task wanting a
private virtual memory context must call contextSetup( ). A sample task to test the
code is included.
/* contextExample.h - header file for vm contexts used by switch hooks */
#define NUM_PAGES (3)
/* context.c - use context switch hooks to make task private context current */
#include "vxWorks.h"
#include "vmLib.h"
#include "semLib.h"
473
12
VxWorks 5.5
Programmers Guide
#include
#include
#include
#include
"taskLib.h"
"taskHookLib.h"
"memLib.h"
"contextExample.h"
474
12
Virtual Memory Interface
475
12
VxWorks 5.5
Programmers Guide
/*
* privContextSwitch - routine to be executed on a context switch
*
* If old task had private context, save it. If new task has private
* context, install it.
*/
void privContextSwitch
(
WIND_TCB *pOldTcb,
WIND_TCB *pNewTcb
)
{
VM_CONTEXT_ID pContext = NULL;
/* If previous task had private context, save it--reset previous context. */
if (pOldTcb->spare1)
{
pContext = (VM_CONTEXT_ID) pOldTcb->spare1;
pOldTcb->spare1 = (int) vmCurrentGet ();
/* restore old context */
vmCurrentSet (pContext);
}
/*
* If next task has private context, map new context and save previous
* context in tasks TCB.
*/
if (pNewTcb->spare1)
{
pContext = (VM_CONTEXT_ID) pNewTcb->spare1;
pNewTcb->spare1 = (int) vmCurrentGet();
/* install new tasks context */
vmCurrentSet (pContext);
}
}
/* taskExample.h - header file for testing VM contexts used by switch hook */
/* This code is used by the sample task. */
#define MAX (10000000)
typedef struct myStuff {
int stuff;
int myStuff;
} MY_DATA;
476
12
Virtual Memory Interface
"vxWorks.h"
"memLib.h"
"taskLib.h"
"stdio.h"
"vmLib.h"
"taskExample.h"
477
12
VxWorks 5.5
Programmers Guide
pMem->stuff = val;
pMem->myStuff = val / 2;
/* make sure can access global virtual memory */
printf (string);
taskDelay (sysClkRateGet() * 10);
}
}
return (OK);
}
/*
* testVmContextGet - return a tasks virtual memory context stored in TCB
*
* Used with vmContextShow()1 to display a tasks virtual memory context.
* For example, from the shell, type:
*
-> tid = sp (testTask)
*
-> vmContextShow (testVmContextGet (tid))
*/
VM_CONTEXT_ID testVmContextGet
(
UINT tid
)
{
return ((VM_CONTEXT_ID) ((taskTcb (tid))->spare1));
}
478
12
Virtual Memory Interface
Thus the value of the data used by the task on CPU 1 is the old value and does not
reflect the modifications done by the task on CPU 0; that value is still in CPU 0s
data cache [2].
Figure 12-3
CPU 0
(task executes first)
Access and
modify myVal.
Cache myVal.
[2]
Data
Cache
[1]
25
myVal
myVal = 100
[3]
12
Access myVal;
myVal = 25
(not the value
of 100 just
set by CPU0).
Memory
CPU 1
(task executes second)
479
VxWorks 5.5
Programmers Guide
routine, the data is made writable for the duration of the routine, and on exit, the
memory is set to VM_STATE_WRITABLE_NOT.
Example 12-2
Nonwritable Memory
In this code example, to modify the data structure pointed to by pData, a task must
call dataModify( ). This routine makes the memory writable, modifies the data,
and sets the memory back to nonwritable. If a task tries to read the memory, it is
successful; however, if it tries to modify the data outside of dataModify( ), a bus
error occurs.
/* privateCode.h - header file to make data writable from routine only */
#define MAX 1024
typedef struct myData
{
char stuff[MAX];
int moreStuff;
} MY_DATA;
/* privateCode.c - uses VM contexts to make data private to a code segment */
#include
#include
#include
#include
"vxWorks.h"
"vmLib.h"
"semLib.h"
"privateCode.h"
MY_DATA * pData;
SEM_ID dataSemId;
int pageSize;
/*
* initData - allocate memory and make it nonwritable
*
* This routine initializes data and should be called only once.
*
*/
STATUS initData (void)
{
pageSize = vmPageSizeGet();
/* create semaphore to protect data */
dataSemId = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);
/* allocate memory = to a page */
pData = (MY_DATA *) valloc (pageSize);
/* initialize data and make it read-only */
480
12
Virtual Memory Interface
481
12
VxWorks 5.5
Programmers Guide
}
semGive (dataSemId);
return (OK);
}
12.5.5 Troubleshooting
If INCLUDE_MMU_FULL_SHOW is included in the project facility VxWorks view,
you can use vmContextShow( ) to display a virtual memory context on the
standard output device. In the following example, the current virtual memory
context is displayed. Virtual addresses between 0x0 and 0x59fff are writeprotected; 0xff800000 through 0xffbfffff are noncacheable; and 0x2000000 through
0x2005fff are private. All valid entries are listed and marked with a V+. Invalid
entries are not listed.
-> vmContextShow 0
value = 0 = 0x0
The output is sent to the standard output device, and looks like the following:
VIRTUAL ADDR
0x0
0x5a000
0x1f9c000
0x1f9e000
0x1fa0000
0x1fa2000
0x1fa4000
0x1faa000
0x1fac000
0x1fb6000
0x1fb8000
0x1fee000
0x1ff0000
0x1ff2000
0x1ff4000
0x1ff6000
0x1ff8000
0x1ffa000
0x1ffc000
0x2000000
0xff800000
0xffe00000
0xfff00000
482
BLOCK LENGTH
0x5a000
0x1f3c000
0x2000
0x2000
0x2000
0x2000
0x6000
0x2000
0xa000
0x2000
0x36000
0x2000
0x2000
0x2000
0x2000
0x2000
0x2000
0x2000
0x4000
0x6000
0x400000
0x20000
0xf0000
PHYSICAL ADDR
0x0
0x5a000
0x1f9c000
0x1f9e000
0x1fa0000
0x1fa2000
0x1fa4000
0x1faa000
0x1fac000
0x1fb6000
0x1fb8000
0x1fee000
0x1ff0000
0x1ff2000
0x1ff4000
0x1ff6000
0x1ff8000
0x1ffa000
0x1ffc000
0x1f96000
0xff800000
0xffe00000
0xfff00000
STATE
W- C+
W+ C+
W+ C+
W- C+
W+ C+
W- C+
W+ C+
W- C+
W+ C+
W- C+
W+ C+
W- C+
W+ C+
W- C+
W+ C+
W- C+
W+ C+
W- C+
W+ C+
W+ C+
W- CW+ C+
W+ C-
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
V+
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
(global)
12
Virtual Memory Interface
12.5.6 Precautions
Memory that is marked as global cannot be remapped using vmMap( ). To add to
global virtual memory, use vmGlobalMap( ). For further information on adding
global virtual memory, see 12.5.2 Private Virtual Memory, p.471.
Performances of MMUs vary across architectures; in fact, some architectures may
cause the system to become non-deterministic. For additional information, see the
architecture-specific documentation for your hardware.
12
483
VxWorks 5.5
Programmers Guide
484
Index
A
abort character (target shell) (CTRL+C) 246247
changing default 247
access routines (POSIX) 76
adapters (VxFusion) 397
see online distIfLib; distIfShow
designing 423
initialization routines 425
input routine 429
I/O control routines 430
network headers 424
send routines 428
startup routines 428
displaying information about 417
working with 417
advertising (VxMP option) 433
AIO, see asynchronous I/O
aio_cancel( ) 124
AIO_CLUST_MAX 123
aio_error( ) 126
testing completion 128
AIO_IO_PRIO_DFLT 124
AIO_IO_STACK_DFLT 124
AIO_IO_TASKS_DFLT 124
aio_read( ) 124
aio_return( ) 126
aiocb, freeing 126
aio_suspend( ) 124
485
VxWorks 5.5
Programmers Guide
attribute (POSIX)
prioceiling attribute 93
protocol attribute 93
attributes (POSIX) 76
contentionscope attribute 77
detachstate attribute 76
inheritsched attribute 77
schedparam attribute 78
schedpolicy attribute 78
specifying 79
stackaddr attribute 76
stacksize attribute 76
AUTOREGISTER_COCLASS
priority schemes, specifying 374
432
B
backplane network, see shared-memory networks
backspace character, see delete character
bd_readyChanged 226
bd_statusChk 227
binary semaphores 3639
BLK_DEV 176
see also block devices
creating a block device 198
fields 179
ready-changes, announcing 211
blkSize 230
block devices 145156
see also BLK_DEV; direct-access devices; disks;
SCSI devices; SEQ_DEV; sequential
devices
adding 163
code example 203
creating 198
defined 158
file systems, and 193240
internal structure 176190
device creation routine 178
device reset routine 184
driver support libraries 190
drivers 160
I/O control routine 183
initialization routine 178
486
C
C++ development
C and C++, referencing symbols between
complex numbers 290
exception handling 284
iostreams 290
Run-Time Type Information (RTTI) 286
Standard Template library (STL) 291
strings 290
C++ support 275293
see also iostreams (C++)
code examples
template instantiation 283
configuring 276
Diab compiler, for 277
GNU compiler, for 277
munching 278
static constructors 280
static destructors 280
template instantiation 281284
cache
see also data cache
see online cacheLib
coherency 172
copyback mode 172
writethrough mode 172
276
Index
CACHE_DMA_FLUSH 174
CACHE_DMA_INVALIDATE 174
CACHE_DMA_PHYS_TO_VIRT 175
CACHE_DMA_VIRT_TO_PHYS 174
CACHE_FUNCS structure 174
cacheDmaMalloc( ) 174
cacheFlush( ) 173
cacheInvalidate( ) 173
cancelling threads (POSIX) 80
cardDetected 321
CBIO interface 141
see online cbioLib; dcacheCbio; dpartCbio;
ramDiskCbio
disk cache 141
disk changes, detection of 142
disk I/O efficiency 141142
disk partition handler 143
RAM disks 144
CComBSTR 390
CComClassFactory 386
CComCoClass 387
CComCoClass class (DCOM) 352
CComObject 388
CComObject class (DCOM) 353
CComObjectRoot 386
CComObjectRoot class (DCOM) 351
CComPtr 389
CComVariant 393
CD-ROM devices 234
cdromFs file systems 234
see online cdromFsLib
CFI_DEBUG 324
cfiscs.c 324
character devices 158
see also drivers
adding 162
driver internal structure 160
naming 110
characters, control (CTRL+x)
target shell 245
tty 133
checkStack( ) 67
client-server communications 5052
CLOCK_REALTIME 74
clocks
487
IX
VxWorks 5.5
Programmers Guide
488
Index
CONTIG_MAX 217
control block (AIO) 125
fields 125
control characters (CTRL+x)
target shell 245
tty 133
conventions
coding 3
device naming 109
documentation 35
file naming 109
task names 15
copyback mode, data cache 172
counting semaphores 43, 86
cplusCtors( ) 281
cplusStratShow( ) 281
cplusXtorSet( ) 281
crashes during initialization 467
creat( ) 114
CTRL+C (abort) 246
CTRL+C (target shell abort) 134
CTRL+D (end-of-file) 134
CTRL+H
delete character
target shell 245
tty 133
CTRL+Q (resume)
target shell 245
tty 134
CTRL+S (suspend)
target shell 245
tty 134
CTRL+U (delete line)
target shell 245
tty 133
CTRL+X (reboot)
target shell 245
tty 134
D
daemons
network tNetTask 30
remote login tRlogind 31
RPC tPortmapd 31
target agent tWdbTask 30
telnet tTelnetd 31
data cache
see also cache; cacheLib(1)
coherency 172
code examples 174
device drivers 172
copyback mode 172
disabling for interprocessor
communication 478479
flushing 173
invalidating 173
shared-memory objects (VxMP option) 454
writethrough mode 172
data structures, shared 32
datagrams 54
see also sockets; UDP
dbgHelp command 245
dbgInit( )
abort facility 247
dcacheDevCreate( ) 198, 313
DCOM (VxDCOM option)
see also CoClasses; COM; Wind Object
Template Library
applications, writing 377385
client code 380
differences between COM and
DCOM APIs 378
querying the server 383
server code 378
VX_FP_TASK 377
ATL, comparison with 385394
OPC interfaces 376
priority schemes 374
choosing 374
priority level 375
propagation, configuring 375
PS_CLNT_PROPAGATED 374
PS_DEFAULT 374
PS_SVR_ASSIGNED 374
properties parameters
VXDCOM_AUTHN_LEVEL 358
VXDCOM_BSTR_POLICY 358
489
IX
VxWorks 5.5
Programmers Guide
VXDCOM_CLIENT_PRIORITY_
PROPAGATION 358
VXDCOM_DYNAMIC_THREADS 359
VXDCOM_OBJECT_EXPORTER_
PORT_NUMBER 359
VXDCOM_SCM_STACK_SIZE 359
VXDCOM_STACK_SIZE 359
VXDCOM_STATIC_THREADS 360
VXDCOM_THREAD_PRIORITY 360
properties parameters, configuring 358
threadpools, using 376
tools 348
Wind Object Template Library
(WOTL) 350357
DCOM wizard
directional attributes 373
non-automation types 363
DEBUG_PRINT 324
debugging
error status values 2224
target shell 246
virtual memory (VxVMI option) 482
DECLARE_CLASSFACTORY_SINGLETON
class 354
delayed tasks 9
delayed-suspended tasks 9
delete character (CTRL+H)
target shell 245
tty 133
delete( ) 115
delete-line character (CTRL+U)
target shell 245
tty 133
demangler
GNU Library General Public License 249
DEV_HDR 163
device descriptors 162
device header 162
device list 162
devices
see also block devices; character devices; directaccess devices; drivers and specific
device types
accessing 109
adding 162
490
block 145156
flash memory 295344
character 158
creating
NFS 138
non-NFS 140
pipes 136
RAM 145
default 110
dosFs 110
internal structure 162
naming 109
network 138
NFS 138
non-NFS 139
pipes 136
pseudo-memory 137
RAM disk 145
SCSI 146156
serial I/O (terminal and pseudo-terminal) 132
sockets 156
working with, in VxWorks 131156
direct-access devices 176
see also block devices
initializing for rawFs 224
internal structure
read routine 181
write routine 182
RAM disks 145
disks
see also block devices; dosFs file systems; rawFs
file systems
changing
device drivers, and 186
dosFs file systems 210
rawFs file systems 225
disk cache (CBIO) 141
file systems, and 193240
initialized, using (dosFs) 201
mounting volumes 225
organization (rawFs) 223
partition handler 143
RAM 145
synchronizing
dosFs file systems 211
Index
491
IX
VxWorks 5.5
Programmers Guide
disk volume
configuration data, displaying 211
mounting 195, 203
disks, changing 210
ready-change mechanism 211
dosFs devices, creating 201
FAT tables 202
file attributes 213
inconsistencies, data structure 219
initialized disks, using 201
initializing 198
ioctl( ) requests, supported 219
MSFT Long Names 202
open( ), creating files with 114
partitions, creating and mounting 198
sectors 194
short names format (8.3) 202
starting I/O 213
subdirectories
creating 211
removing 212
synchronizing volumes 211
TrueFFS flash file systems 300
volumes, formatting 201
VxWorks long names format 202
dosFsChkDsk( ) 195
dosFsDevCreate( ) 195, 201, 313
dosFsDevInit( ) 149
dosFsDrvNum global variable 198
dosFsFmtLib 194
dosFsLib 194
dosFsShow( ) 211
dosFsVolFormat( ) 201
downloading, see loading
dpartCbioLib 143
dpartDevCreate( ) 198
driver number 161
driver table 161
drivers 109
see also devices and specific driver types
asynchronous I/O 123
code example 158
data cache coherency 172
file systems, and 193240
hardware options, changing 135
492
installing 161
internal structure 160
interrupt service routine limitations
libraries, support 190
memory 137
NFS 138
non-NFS network 139
pipe 136
pty (pseudo-terminal) 132
RAM disk 145
SCSI 146156
tty (terminal) 132
VxWorks, available in 131
69
E
edit mode (target shell) 245
encryption
login password 249
end-of-file character (CTRL+D) 134
__errno( ) 23
errno 2224, 69
and task contexts 23
example 24
return values 2324
error status values 2224
ESCAPE key (target shell) 245
ev_receive( ) 59
ev_send( ) 59
eventClear( ) 62
eventReceive( ) 62
events 5763
defined 57
pSOS events 5859
API 59
comparison with VxWorks events API 63
enhancements in VxWorks events 61
freeing resources 59
registering for 59
sending and receiving 58
waiting for 58
VxWorks events 6063
API 62
comparison with pSOS events API 63
Index
F
FAT tables (dosFs)
supported formats 202
fclose( ) 121
fd table 164
fd, see file descriptors
FD_CLR 117
FD_ISSET 117
FD_SET 117
FD_ZERO 117
fdopen( ) 121
fdprintf( ) 122
FIFO
message queues, Wind 48
POSIX 82
file descriptors (fd) 111
see also files
see online ioLib
device drivers, and 163
fd table 164
freeing obsolete (rawFs) 226
internal structure 163
pending on multiple (select facility)
reclaiming 111
redirection 112
standard input/output/error 112
file pointers (fp) 121
file systems 193240
117
493
IX
VxWorks 5.5
Programmers Guide
FIOFSTATGET 220
FTP or RSH, using with 140
NFS client devices, using with 139
FIOGETNAME 220
FTP or RSH, using with 140
NFS client devices, using with 139
pipes, using with 137
tty devices, using with 135
FIOGETOPTIONS 135
FIOLABELGET 220
FIOLABELSET 220
FIOMKDIR 211
FIOMOVE 220
FIONCONTIG 220
FIONFREE 220
FIONMSGS 137
FIONREAD 220
FTP or RSH, using with 140
NFS client devices, using with 139
pipes, using with 137
tty devices, using with 135
FIONWRITE 135
FIOREADDIR 220
FTP or RSH, using with 140
NFS client devices, using with 139
FIORENAME 220
FIORMDIR 212
FIOSEEK 225
FTP or RSH, using with 140
memory drivers, using with 138
NFS client devices, using with 139
FIOSELECT 170
FIOSETOPTIONS
tty devices, using with 135
tty options, setting 132
FIOSYNC
FTP or RSH, using with 140
NFS client devices, using with 139
rawFs file systems, and 227
tapeFs file systems, and 232
FIOTRUNC 215
FIOUNMOUNT 226
FIOUNSELECT 170
FIOWHERE 220
FTP or RSH, using with 140
494
Index
G
getc( ) 121
global variables 27
GNU Library General Public License
demangler 249
GPL (General Public License)
demangler 249
group message queues (VxFusion) 397
see online msgQDistGrpLib;
msgQDistGrpShow
adding members 414
creating 414
deleting 415
displaying information about 416
working with 414
H
hardware
interrupts, see interrupt service routines
heartbeat, shared-memory 461
434
I
I/O system
asynchronous I/O
aioPxLib 73
IChannelHook interface (DCOM) 375
IClassFactory interface (COM) 352
IDL, see Interface Definition Language
IExample interface (DCOM) 353
import directive (DCOM) 368
INCLUDE_ATA
configuring dosFs file systems 197
INCLUDE_CACHE_ENABLE 454
INCLUDE_CBIO 197
INCLUDE_CBIO_DCACHE 141
INCLUDE_CBIO_DPART 141
INCLUDE_CBIO_MAIN 141
INCLUDE_CBIO_RAMDISK 141
INCLUDE_CDROMFS 234
INCLUDE_CPLUS 276, 277
INCLUDE_CPLUS_COMPLEX 277
INCLUDE_CPLUS_COMPLEX_IO 277
INCLUDE_CPLUS_IOSTREAMS 277
INCLUDE_CPLUS_IOSTREAMS_FULL 277
INCLUDE_CPLUS_LANG 276
INCLUDE_CPLUS_STL 277
INCLUDE_CPLUS_STRING 277
INCLUDE_CPLUS_STRING_IO 277
INCLUDE_CTORS_DTORS 276
INCLUDE_DISK_CACHE 197
creating a disk cache 198
INCLUDE_DISK_PART 197
495
IX
VxWorks 5.5
Programmers Guide
496
INCLUDE_SCSI2 147
INCLUDE_SECURITY 249
INCLUDE_SEM_SHOW 437
INCLUDE_SHELL 244
INCLUDE_SHELL_BANNER 244
INCLUDE_SIGNALS 55, 56
INCLUDE_SM_OBJ 454, 459
INCLUDE_TAPEFS 228
INCLUDE_TAR 197
INCLUDE_TELNET 248
INCLUDE_TFFS 302
INCLUDE_TFFS_BOOT_IMAGE 303
INCLUDE_TFFS_SHOW 303
INCLUDE_TL_FTL 305
INCLUDE_TL_SSFDC 305
INCLUDE_USR_MEMDRV 137
INCLUDE_VXFUSION 396
INCLUDE_VXFUSION_DIST_MSG_
Q_SHOW 396
INCLUDE_VXFUSION_DIST_NAME_
DB_SHOW 396
INCLUDE_VXFUSION_GRP_MSG_
Q_SHOW 396
INCLUDE_VXFUSION_IF_SHOW 396
INCLUDE_WDB_TSFS 239
INCLUDEMTD_WAMD 304
initialization
shared-memory objects
(VxMP option) 456459, 461
virtual memory (VxVMI option) 470
initialization routines
adapters, designing (VxFusion) 425
block devices 178
initializing
asynchronous I/O (POSIX) 123
dosFs file system 198
rawFs file systems 223
SCSI interface 149
tapeFs file systems 229
VxFusion (distributed message queues)
installing drivers 161
instantiation, template (C++) 281284
intConnect( ) 66
intCount( ) 66
400
Index
locking 33
shared-memory objects (VxMP option) 455
task-level code, communicating to 71
VMEbus 67
intertask communications 3256
network 5354
intLevelSet( ) 66
intLock( ) 66
intLockLevelSet( ) 70
intUnlock( ) 66
intVecBaseGet( ) 66
intVecBaseSet( ) 66
intVecGet( ) 66
intVecSet( ) 66
I/O system 107192
see also asynchronous I/O; ioctl( ); USB
asynchronous I/O 123131
basic I/O (ioLib) 111119
buffered I/O 120
control functions (ioctl( )) 116
differences between VxWorks and host
system 156
fd table 164
formatted I/O (fioLib) 122
internal structure 157191
memory, accessing 137
message logging 122
PCI (Peripheral Component Interconnect) 192
PCMCIA 191
redirection 112
serial devices 132
stdio package (ansiStdio) 120
ioctl( ) 116
dosFs file system support 219
functions
FTP, using with 140
memory drivers, using with 137
NFS client devices, using with 139
pipes, using with 136
RSH, using with 140
tty devices, using with 135
non-NFS devices 140
raw file system support 227
tapeFs file system support 232
tty options, setting 132
497
IX
VxWorks 5.5
Programmers Guide
ioDefPathGet( ) 110
ioDefPathSet( ) 110
ioGlobalStdSet( ) 112
iosDevAdd( ) 162
iosDevFind( ) 163
iosDrvInstall( ) 161
dosFs, and 198
iostreams (C++) 290
ioTaskStdSet( ) 112
ISR, see interrupt service routines
IUnknown interface (DCOM) 351
K
kernel
see also Wind facilities
and multitasking 8
POSIX and Wind features, comparison of
message queues 9495
scheduling 81
semaphores 86
priority levels 10
kernelTimeSlice( ) 11, 12
keyboard shortcuts
target shell 245
tty characters 133
kill( ) 55, 56, 105
killing
target shell, see abort character
tasks 18
L
-L option (TSFS) 240
latency
interrupt locks 33
preemptive locks 34
Library General Public License
demangler restrictions 249
line editor (target shell) 245
line mode (tty devices) 133
selecting 132
498
73
lio_listio( ) 124
lkup( )
help, getting 245
loader, target-resident 250261
loading
object modules 245
local objects 431
locking
interrupts 33
page (POSIX) 75
semaphores 85
spin-lock mechanism (VxMP option)
target shell access 248
task preemptive locks 13, 34
logging facilities 122
and interrupt service routines 69
task tLogTask 30
login
password, encrypting 249
remote
daemon tRlogind 31
security 248249
shell, accessing target 248
loginUserAdd( ) 249
longjmp( ) 25
452453
M
malloc( )
interrupt service routine limitations 69
MAX_AIO_SYS_TASKS 124
MAX_LIO_CALLS 123
MEM_BLOCK_CHECK 451
memory
driver (memDrv) 137
flash 295344
advantages and limitations 295
block allocation 338
boot image in 309312
comparison with other storage media
data clusters 338
erase cycles 340
fallow region 309
fault recovery 343
295
Index
writing 327337
component, defining as 335
identification routine 336
memPartOptionsSet( ) 451
memPartSmCreate( ) 449
message logging, see logging facilities
message queues 4752
see also msgQLib(1)
and VxWorks events 51
client-server example 50
displaying attributes 50, 97
and interrupt service routines 71
POSIX 9495
see also mqPxLib(1)
attributes 9597
code examples
attributes, examining 9597
checking for waiting
message 101105
communicating by message
queue 98100
notifying tasks 100105
unlinking 98
Wind facilities, differences from 9495
priority setting 49
shared (VxMP option) 439444
code example 441
creating 440
local message queues, differences
from 440
Wind 4850
code example 49
creating 48
deleting 48
queueing order 48
receiving messages 48
sending messages 48
timing out 49
waiting tasks 48
ml( ) 246
mlock( ) 75
mlockall( ) 75
mmanPxLib 75
499
IX
VxWorks 5.5
Programmers Guide
MMU
see also virtual memory - VxVMI option;
vmLib(1)
shared-memory objects (VxMP option) 457
using programmatically 469483
modules, see component modules; object modules
mounting volumes
dosFs file systems 195, 203
rawFs file systems 225
tapeFs file systems 231
mq_close( ) 94, 98
mq_getattr( ) 94, 95
mq_notify( ) 94, 100105
mq_open( ) 94, 98
mq_receive( ) 94, 98
mq_send( ) 94, 98
mq_setattr( ) 94, 95
mq_unlink( ) 94, 98
mqPxLib 94
mqPxLibInit( ) 94
mqPxLibInit( ) 94
mr( ) 246
MS-DOS file systems, see dosFs file systems
MSFT Long Names format 202
msgQCreate( ) 48
msgQDelete( ) 48
msgQDistCreate( ) 409
msgQDistDelete( ) 409
msgQDistGrpAdd( ) 415
msgQDistGrpShow( ) 416
msgQDistReceive( ) 412
msgQDistSend( ) 410
msgQEvStart( ) 63
msgQEvStop( ) 64
msgQReceive( ) 48
msgQSend( ) 62
msgQSend( ) 48
msgQShow( )
distributed message queues, displaying
contents of 413
msgQShow( ) 441
msgQSmCreate( ) 440
mt_count 233
mt_op 233
MTBSF 233
500
MTBSR 233
MTD component
adding to project facility 336
MTD, see Memory Technology Driver
mtdTable[ ] 336
MTEOM 234
MTERASE 234
MTFSF 233
MTFSR 233
MTIOCTOP operation 233
MTNBSF 234
MTNOP 234
MTOFFL 234
MTOP structure 233
MTRETEN 234
MTREW 233
MTWEOF 232
multitasking 8, 25
example 29
munching (C++) 278
munlock( ) 75
munlockall( ) 75
mutexes (POSIX) 92
mutual exclusion 3334
see also semLib(1)
code example 3738
counting semaphores 43
interrupt locks 33
preemptive locks 34
and reentrancy 27
Wind semaphores 4043
binary 3738
deletion safety 42
priority inheritance 41
priority inversion 40
recursive use 42
N
name database (VxMP option) 433435
adding objects 433
displaying 434
named semaphores (POSIX) 85
using 8992
Index
nanosleep( ) 20
using 74
netDevCreate( ) 140
netDrv
compared with TSFS 239
netDrv driver 139
network devices
see also FTP; NFS; RSH
NFS 138
non-NFS 139
Network File System, see NFS
network task tNetTask 30
networks
intertask communications 5354
transparency 138
NFS (Network File System)
see online nfsDrv; nfsLib
authentication parameters 139
devices 138
creating 138
naming 110
open( ), creating files with 114
ioctl functions, and 139
transparency 138
nfsAuthUnixPrompt( ) 139
nfsAuthUnixSet( ) 139
nfsDrv driver 138
nfsMount( ) 138
NO_FTL_FORMAT 308
non-block devices, see character devices
noOfDrives 320
ntohl( )
shared-memory objects (VxMP option)
NUM_RAWFS_FILES 223
NVRAM 309
O
O_CREAT 212
O_NONBLOCK 95
O_CREAT 89
O_EXCL 89
O_NONBLOCK 98
object ID (VxMP option)
433
object modules
see also application modules
loading dynamically 245
online documentation 4
OPC interfaces (DCOM) 376
proxy and stub files, handling 376
open( ) 113
access flags 113
example 165
files asynchronously, accessing 123
files with, creating 114
subdirectories, creating 211
opendir( ) 212
operating system 775
OPT_7_BIT 132
OPT_ABORT 133
OPT_CRMOD 132
OPT_ECHO 132
OPT_LINE 132
OPT_MON_TRAP 133
OPT_RAW 133
OPT_TANDEM 132
OPT_TERMINAL 133
optional components (TrueFFS)
options 303
optional VxWorks products
VxMP shared-memory objects 431463
VxVMI virtual memory 466483
ORPC support 349
434
P
page locking 75
see also mmanPxLib(1)
page states (VxVMI option) 470
paging 74
partitions, disk
code examples
configuring disk partitions 200
creating disk partitions 199
formatting disk partitions 199
initializing multiple partitions 200
password encryption
login 249
501
IX
VxWorks 5.5
Programmers Guide
pause( ) 56
PCI (Peripheral Component Interconnect) 192
see online pciConfigLib; pciConfigShow;
pciInitLib
PCMCIA 191, 315
see online pcmciaLib; pcmciaShow
pdHelp command 245
pended tasks 9
pended-suspended tasks 9
PHYS_MEM_DESC 467
pipeDevCreate( ) 53
pipes 53
see online pipeDrv
interrupt service routines 71
ioctl functions, and 136
ISRs, writing from 136
select( ), using with 53
polling
shared-memory objects (VxMP option) 455
POSIX
see also asynchronous I/O
asynchronous I/O 123
clocks 7374
see also clockLib(1)
file truncation 116
and kernel 73
memory-locking interface 7475
message queues 9495
see also message queues; mqPxLib(1)
mutex attributes 92
prioceiling attribute 93
protocol attribute 93
page locking 75
see also mmanPxLib(1)
paging 74
priority limits, getting task 84
priority numbering 82
scheduling 8185
see also scheduling; schedPxLib(1)
semaphores 8592
see also semaphores; semPxLib(1)
signal functions 105106
see also signals; sigLib(1)
routines 56
swapping 74
502
82
Index
pthread_attr_getstackaddr( ) 76
pthread_attr_getstacksize( ) 76
pthread_attr_setdetachstate( ) 77
pthread_attr_setinheritsched( ) 77
pthread_attr_setschedparam( ) 78
pthread_attr_setscope( ) 77
pthread_attr_setstackaddr( ) 76
pthread_attr_setstacksize( ) 76
pthread_attr_t 76
pthread_cleanup_pop( ) 80, 81
pthread_cleanup_push( ) 80, 81
PTHREAD_CREATE_DETACHED 77
PTHREAD_CREATE_JOINABLE 77
PTHREAD_EXPLICIT_SCHED 77
pthread_getschedparam( ) 78
pthread_getspecific( ) 80
PTHREAD_INHERIT_SCHED 77
pthread_key_create( ) 80
pthread_key_delete( ) 80
pthread_mutex_getprioceiling( ) 93
pthread_mutex_setprioceiling( ) 93
pthread_mutexattr_getprioceiling( ) 93
pthread_mutexattr_getprotocol( ) 93
pthread_mutexattr_setprioceiling( ) 93
pthread_mutexattr_setprotocol( ) 93
pthread_mutexattr_t 92
PTHREAD_PRIO_INHERIT 93
PTHREAD_PRIO_PROTECT 93
PTHREAD_SCOPE_PROCESS 77
PTHREAD_SCOPE_SYSTEM 77
pthread_setcancelstate( ) 80
pthread_setcanceltype( ) 80
pthread_setschedparam( ) 78
pthread_setspecific( ) 80
pty devices 132
see online ptyDrv
pure code 27
putc( ) 121
Q
q_notify( ) 60
q_vnotify( ) 60
queued signals 105106
queues
see also message queues
ordering (FIFO vs. priority)
semaphore wait 45
4548
R
-R option (TSFS) 240
raise( ) 56
RAM disks 144
see online ramDrv
code example 209
creating 145
drivers 145
naming 145
ramDevCreate( ) 145
raw mode (tty devices) 133
rawFs file systems 223228
see online rawFsLib
disk changes 225
ready-change mechanism 226
unmounting volumes 225
disk organization 223
disk volume, mounting 225
fds, freeing obsolete 226
initializing 223
ioctl( ) requests, support for 227
starting I/O 225
synchronizing disks 227
rawFsDevInit( ) 224
rawFsDrvNum global variable 224
rawFsInit( ) 223
rawFsLib 226
rawFsReadyChange( ) 226
rawFsVolUnmount( ) 225
interrupt handling 226
read( ) 115
example 168
readdir( ) 212
ready tasks 9
ready-change mechanism
dosFs file systems 211
rawFs file systems 226
503
IX
VxWorks 5.5
Programmers Guide
504
rfaWriteProtected 319
rfaWriteProtected( ) 322
ring buffers 69, 71
rlogin (UNIX) 248
ROM monitor trap (CTRL+X)
target shell 245
tty 134
root task tUsrRoot 30
round-robin scheduling
defined 12
using 8485
routines
scheduling, for 81
RPC (Remote Procedure Calls) 54
daemon tPortmapd 31
RSH (Remote Shell protocol)
ioctl functions, and 140
network devices for, creating 140
Run-Time Type Information (RTTI) 286
-RW option (TSFS) 240
S
SAFEARRAY and SAFEARRAY * (DCOM)
supported COM features 364
supported DCOM features 365
Scalable command set (SCS) 323
scanf( ) 122
SCHED_FIFO 84
sched_get_priority_max( ) 84
sched_get_priority_max( ) 81
sched_get_priority_min( ) 84
sched_get_priority_min( ) 81
sched_getparam( )
scheduling parameters, describing 78
sched_getparam( ) 81
sched_getscheduler( ) 81, 84
SCHED_RR 84
sched_rr_get_interval( ) 84
sched_rr_get_interval( ) 81
sched_setparam( )
scheduling parameters, describing 78
sched_setparam( ) 81, 83
sched_setscheduler( ) 81, 83
364
Index
sched_yield( ) 81
schedPxLib 81, 82
scheduling 1014
POSIX 8185
see also schedPxLib(1)
algorithms 82
code example 84
FIFO 82, 84
policy, displaying current 84
preemptive priority 84
priority limits 84
priority numbering 82
round-robin 8485
routines for 81
time slicing 84
Wind facilities, differences from 81
Wind
preemptive locks 13, 34
preemptive priority 11
round-robin 12
SCSI devices 146156
see online scsiLib
booting dosFs file systems using 221
booting from
ROM size, adjusting 148
bus failure 155
code example 209
configuring 147155
code examples 151
options 150
initializing support 149
libraries, supporting 148
SCSI bus ID
changing 155
configuring 148
SCSI-1 vs. SCSI-2 148
tagged command queuing 151
troubleshooting 155
VxWorks image size, affecting 148
wide data transfers 151
SCSI_AUTO_CONFIG 147
SCSI_OPTIONS structure 150
SCSI_TAG_HEAD_OF_QUEUE 151
SCSI_TAG_ORDERED 151
SCSI_TAG_SIMPLE 151
SCSI_TAG_UNTAGGED 151
scsi1Lib 148
scsi2Lib 148
scsiBlkDevCreate( ) 149
scsiCommonLib 149
scsiDirectLib 149
scsiLib 148
scsiPhysDevCreate( ) 149
scsiSeqDevCreate( ) 229
scsiSeqLib 149
scsiTargetOptionsSet( ) 150
SCSI bus failure 156
security 248249
TSFS 239
SEL_WAKEUP_LIST 169
SEL_WAKEUP_NODE 170
select facility 117
see online selectLib
code example 118
driver support of select( )
macros 117
select( ) 117
implementing 168
select( )
and pipes 53
selNodeAdd( ) 170
selNodeDelete( ) 170
selWakeup( ) 170
selWakeupAll( ) 170
selWakeupListInit( ) 169
selWakeupType( ) 170
sem_close( ) 86, 90
SEM_DELETE_SAFE 42
sem_destroy( ) 86
sem_getvalue( ) 86
sem_init( ) 86, 87
SEM_INVERSION_SAFE 41
sem_open( ) 86, 89
sem_post( ) 86
sem_trywait( ) 86
sem_unlink( ) 86, 90
sem_wait( ) 86
505
170
IX
VxWorks 5.5
Programmers Guide
semaphores 3492
and VxWorks events 4547
see also semLib(1)
counting 86
example 43
deleting 36, 87
giving and taking 3637, 85
and interrupt service routines 71, 69
locking 85
POSIX 8592
see also semPxLib(1)
named 85, 8992
code example 90
unnamed 85, 87, 8789
code example 88
Wind facilities, differences from 86
posting 85
recursive 42
code example 42
shared (VxMP option) 435439
code example 437
creating 436
displaying information about 437
local semaphores, differences from 436
synchronization 34, 43
code example 3839
unlocking 85
waiting 85
Wind 3447
binary 3639
code example 3738
control 3536
counting 43
mutual exclusion 3738, 4043
queuing 45
synchronization 3839
timing out 44
semBCreate( ) 35
semBSmCreate( ) (VxMP option) 436
semCCreate( ) 35
semCSmCreate( ) (VxMP option) 436
semDelete( ) 35
shared semaphores (VxMP option) 436
semEvStart( ) 63
semEvStop( ) 63
506
semFlush( ) 35, 40
semGive( ) 62
semGive( ) 35
semInfo( ) 437
semMCreate( ) 35
semPxLib 85
semPxLibInit( ) 86
semShow( ) 437
semTake( ) 35
SEQ_DEV 176
see also sequential devices
fields 180
tapeFs file system, using with 229
sequential devices 176
see also block devices; SEQ_DEV; tape devices;
tapeFs file systems
initializing for tapeFs 229
internal structure
load/unload routine 189
read routine 182
read-block-limits routine 188
reserve routine 187
tape erasing routine 190
tape release routine 188
tape rewind routine 187
tape spacing routine 189
write routine 183
write-file-marks routine 186
serial drivers 132
set_terminate( ) (C++) 286
setjmp( ) 25
setWindow( ) 318
shared code 25
shared data structures 32
shared message queues (VxMP option) 439444
code example 441
creating 440
displaying queue status 441
local message queues, differences from 440
shared semaphores (VxMP option) 435439
code example 437
creating 436
displaying information about 437
local semaphores, differences from 436
shared-memory allocator (VxMP option) 444451
Index
shared-memory anchor
shared-memory objects, configuring (VxMP
option) 454455
shared-memory networks
shared-memory objects, working with 454
shared-memory objects (VxMP option) 431463
advertising 433
anchor, configuring shared-memory 454455
cacheability 454, 457
configuring 454461
constants 458
multiprocessor system 459
displaying number of used objects 459
heartbeat 461
troubleshooting, for 462
initializing 456459, 461
interrupt latency 453
interrupt service routines 453
interrupts
bus 455
mailbox 455
limitations 453454
locking (spin-lock mechanism) 452453
memory
allocating 444451
running out of 453
memory layout 456
message queues, shared 439444
see also shared message queues
code example 441
name database 433435
object ID 433
partitions 444451
routines 445
side effects 451
system 444448
code example 446
user-created 445, 449451
code example 449
polling 455
semaphores, shared 435439
see also shared semaphores (VxMP option)
code example 437
shared-memory networks, working with 454
shared-memory pool 456
507
IX
VxWorks 5.5
Programmers Guide
SM_ANCHOR_ADRS 455
SM_INT_BUS 455
SM_INT_MAILBOX 455
SM_INT_NONE 455
SM_INT_TYPE 455
sm_notify( ) 59
SM_OBJ_MAX_MEM_PART 458
SM_OBJ_MAX_MSG_Q 458
SM_OBJ_MAX_NAME 458
SM_OBJ_MAX_SEM 458
SM_OBJ_MAX_TASK 458
SM_OBJ_MAX_TRIES 453
SM_OBJ_MEM_SIZE 458
SM_TAS_HARD 452
SM_TAS_TYPE 452
small computer system interface, see SCSI devices
smCpuInfoGet( ) (VxMP option) 455
smIfVerbose global variable (VxMP) 463
smMemAddToPool( ) (VxMP option) 446
smMemCalloc( ) (VxMP option) 446
smMemFindMax( ) (VxMP option) 446
smMemFree( ) (VxMP option) 446
smMemMalloc( ) (VxMP option) 446
smMemOptionsSet( ) (VxMP option) 446, 451
smMemRealloc( ) (VxMP option) 446
smMemShow( ) (VxMP option) 446
smNameAdd( ) (VxMP option) 433
smNameFind( ) (VxMP option) 433
smNameFindByValue( ) (VxMP option) 433
smNameRemove( ) (VxMP option) 433
smNameShow( ) (VxMP option) 433
smObjAttach( ) (VxMP option) 461
smObjInit( ) (VxMP option) 461
smObjSetup( ) (VxMP option) 461
smObjShow( ) (VxMP option) 459
troubleshooting, for 462
smObjTimeoutLogEnable( ) (VxMP option) 462
socket component drivers (TrueFFS)
translation layer 300
socket( ) 156
sockets 5354
I/O devices, as 156
TSFS 238
spawning tasks 1415, 29
spin-lock mechanism (VxMP option) 452453
508
458
Index
sysTffs.c 300
sysTffsFormat( ) 310
sysTffsInit( ) 307
T
T_SM_BLOCK 434
T_SM_MSG_Q 434
T_SM_PART_ID 434
T_SM_SEM_B 434
T_SM_SEM_C 434
tape devices
see also sequential devices; tapeFs file systems
changing 232
code examples
block devices, working with 231
SCSI, working with 153
configuring 230
SCSI, supporting 147
volumes
mounting 231
unmounting 232
TAPE_CONFIG 230
tapeFs file systems 228234
code example
block size, working with 231
fixed block size transfers 230
initializing 229
ioctl( ) requests, and 232
mounting tape volumes 231
operating modes 231
SCSI drivers, and 147
sequential devices for, initializing 229
starting I/O 232
tape changes 232
tape organization 229
variable block size transfers 230
tapeFsDevInit( ) 229
tapeFsDrvNum global variable 229
tapeFsInit( ) 229
tapeFsLib 228
tapeFsVolUnmount( ) 232
target agent
task (tWdbTask) 30
509
IX
VxWorks 5.5
Programmers Guide
taskNameToId( ) 16
taskOptionsGet( ) 17
taskOptionsSet( ) 17
taskPriorityGet( ) 17
taskPrioritySet( ) 10, 11
taskRegsGet( ) 17
taskRegsSet( ) 18
taskRestart( ) 20
taskResume( ) 20
tasks 831
blocked 13
contexts 8
creating 14
control blocks 8, 18, 21, 28, 68
creating 1415
delayed 9
delayed-suspended 9
delaying 8, 9, 20, 6465
deleting safely 1819
code example 19
semaphores, using 42
displaying information about 1718
error status values 2224
see also errnoLib(1)
exception handling 2425
see also signals; sigLib(1); excLib(1)
tExcTask 30
executing 19
hooks
see also taskHookLib(1)
extending with 2122
troubleshooting 21
IDs 15
interrupt level, communicating at 71
pipes 136
logging (tLogTask) 30
names 15
automatic 16
network (tNetTask) 30
option parameters 16
pended 9
pended-suspended 9
priority, setting
driver support tasks 14
POSIX 8283
510
Index
taskUnsafe( ) 18, 19
taskVarAdd( ) 28
taskVarDelete( ) 28
taskVarGet( ) 28
taskVarSet( ) 28
TCP (Transmission Control Protocol) 53
telegram buffers (VxFusion) 422
see online distTBufLib
telegrams (VxFusion) 421
reading 429
telnet 248
daemon tTelnetd 31
terminal characters, see control characters
terminate( ) (C++) 286
TFFS_STD_FORMAT_PARAMS 307
tffsBootImagePut( ) 303
tffsConfig.c 311
tffsDevCreate( ) 312
tffsDevFormat( ) 306
tffsDevFormatParams 307
tffsDriveNo argument 307
tffsDrv.h 306
tffsRawio( ) 311
tffsShow( ) 303
tffsShowAll( ) 303
threads (POSIX) 75
attributes 7680
specifying 79
keys 80
private data, accessing 80
terminating 80
time slicing 12
determining interval length 84
timeout
message queues 49
semaphores 44
timeouts
semaphores 44
timers
see also timerLib(1)
message queues, for (Wind) 49
POSIX 7374
semaphores, for (Wind) 44
watchdog 6465
code examples 65
tools
configuration and build 2
tools, target-based development 241274
translation layers (TrueFFS) 296
options 305
transports (VxFusion) 397
troubleshooting
SCSI devices 155
shared-memory objects (VxMP option) 461
TrueFFS flash file systems 295344
boot image region 309312
creating 309
fallow region 309
write-protecting 309
writing to 311
building
boot image region 309312
command-line 313
conditional compilation 305
configuring 300306
device formatting 306
drive mounting 312
examples 313
Memory Technology Driver (MTD) 299
overview 298
socket driver 300
displaying information about 303
drives
attaching to dosFs 312
formatting 307
mounting 312
numbering 307
fallow region 309
ioctl 296
layers
core layer 297
MTD layer 297
socket layer 297
translation layer 297
Memory Technology Driver (MTD)
Common Flash Interface (CFI) 323
component selection 299
devices supported 323
identification routine 328
JEDEC device ID 300
511
IX
VxWorks 5.5
Programmers Guide
writing 327337
socket driver
address mapping 322
PCMCIA 315
register routines 316
Resident Flash Array (RFA) 315
socket registration 320
socket structure 316
socket windowing 322
stub files 315
writing 314323
translation layers 296
write protection 309
write-protecting flash
NVRAM 309
truncation of files 116
tty devices 132
see online tyLib
control characters (CTRL+x) 133
ioctl( ) functions, and 135
line mode 133
selecting 132
options 132
all, setting 133
none, setting 133
raw mode 133
X-on/X-off 132
tyAbortSet( ) 247
tyBackspaceSet( ) 134
tyDeleteLineSet( ) 134
tyEOFSet( ) 134
tyMonitorTrapSet( ) 135
U
UDP (User Datagram Protocol) 54
unnamed semaphores (POSIX) 85, 87, 8789
usrDosFsOld.c 197
usrFdiskPartCreate( ) 198
usrFdiskPartLib 143
usrMmuInit( ) 470
usrScsiConfig( ) 149
usrSmObjInit( ) (VxMP option) 454, 456, 461
usrTffsConfig( ) 312
512
usrVxFusionInit( ) 400
customizing with 401
V
valloc( ) (VxVMI option) 471
variables
global 27
static data 27
task 28
VARIANT and VARIANT * (DCOM) 363
vector tables
exception, write-protecting 468
virtual circuit protocol, see TCP
virtual memory 465483
configuration 466468
mapping 467468
aliasing 472
VxVMI option 466483
configuration 466468
contexts 469471
debugging 482
global 469
initializing 470
page states 470
private 471478
code example 473
restrictions 483
write-protecting 468, 479482
code example 480
VM_CONTEXT 469
VM_PAGE_SIZE 466
VM_STATE_CACHEABLE constants 470
VM_STATE_MASK_CACHEABLE 471
VM_STATE_MASK_VALID 471
VM_STATE_MASK_WRITABLE 471
VM_STATE_VALID 470
VM_STATE_VALID_NOT 470
VM_STATE_WRITABLE 470
VM_STATE_WRITABLE_NOT 470
vmContextCreate( ) (VxVMI option) 471
vmContextShow( ) (VxVMI option) 482
vmCurrentSet( ) (VxVMI option) 471
VMEbus interrupt handling 67
Index
W
WAIT_FOREVER 44
watchdog timers 6465
code examples
creating a timer 65
wdCancel( ) 64, 65
wdCreate( ) 64
wdDelete( ) 64
wdStart( ) 64
widl compiler 360366
command-line use 360
data types 362
automation 362
non-automation 363
generated code 361
Wind facilities 73
message queues 4850
POSIX, differences from 73
message queues 9495
scheduling 81
semaphores 86
scheduling 1014
semaphores 3447
Wind IDL compiler, see widl compiler
wind kernel, see kernel
Wind Object Template Library (WOTL)
classes 350
generated files
macros in 355
reading 354
interface mapping 357
window structure 318
workQPanic 70
write protection 468, 479482
device drivers, and 186
write( ) 115
pipes and ISRs 136
writethrough mode, cache 172
513
IX
350357