RIC(4,F) AIX Technical Reference RIC(4,F)
-------------------------------------------------------------------------------
RIC
PURPOSE
Supports the Realtime Interface Co-processor (RIC) Multiport 2.
DESCRIPTION
The /dev/ric are special files that provide an image of RIC memory. Ric0
refers to the first RIC card, ric1 to the second, ric2 to the third, and ric3
to the fourth RIC card.
Byte address in /dev/ric refer to locations in memory on the appropriate RIC
card. References to non-existent locations cause errors to be returned.
In addition to memory image, /dev/ric has several ioctl commands which are
defined in sys/1386/ric.h.
ICACMD Issues a command to a task on the RIC. The argument of this
ioctl is a pointer to a riccmd structure. This ioctl will not
return until the RIC task has returned an interrupt. The
content of the input buffer is copied into the arg element of
the riccmd structure when the interrupt is received from the RIC
task. A command 3 is sent to the RIC task 0 after copying the
input buffer. This ioctl is primarily intended for sending
commands to RCM.
ICARESET Issues a hardware reset to the RIC card. This command may take
up to 30 seconds to complete, as the RIC card will run its
diagnostic tests. This command also initializes the RCM's
maximum number of tasks, priorities, queues, and timeouts.
ICACMDNOWAIT Performs the same function as ICACMS, but does not wait for an
interrupt from the RIC task, does not copy the input buffer, and
does not send a command 3 to the RIC task upon completion.
ANOUNCETASK Identifies a task so that other device drivers that depend on
the RIC tasks can find the appropriate task on the RIC card.
ICA_STAT1 Retrieves the primary status byte of a specified task on an
ARTIC card. This value is returned in the user provided
argument (the first byte of the arg field of the riccmd
structure; see the header file ric.h)
ICA_STAT2 Retrieves secondary status bytes (the length of the buffer is
task dependent) of a specified task on an ARTIC card. It is
issued in the main module of the application process. Data is
returned via the arg field of the riccmd structure.
Processed November 7, 1990 RIC(4,F) 1
RIC(4,F) AIX Technical Reference RIC(4,F)
ICA_ASYNC Allows an application process (caller) to handle asynchronous
interrupts from a particular task on an ARTIC card. It is
issued by the main module of the application process.
ICA_NOASYNC Undoes the effect of ICA_ASYNC.
ICA_CHKSIG Allows an application process to find out which task on an ARTIC
card has caused an asynchronous interrupt. This command is
normally issued by the signal handler of the application
process.
ICA_QURIC Reports the number of ARTIC cards installed on a machine.
Returned values include slot number, card number, and card type
respectively (see the header file ric.h), via the arg field of
the riccmd structure.
Note: If a card is bad, its type will be UNKNOWNCARD.
SUPPORTING COMMANDS
There are two commands which enable users to reset an ARTIC card and then
download tasks onto it.
To reset a card, enter at the command line:
icareset <icareset <card_number>
where card_number is some integer n which is 0 for the ARTIC card in the lowest
slot number, 1 for the ARTIC card in the slot next to the lowest one, etc.
To load a task on an ARTIC card, enter at the command line:
icaload [-v] <card_number> <task_name> <task_number> [load_option]
where task_name is the file name of a task and task_number is an integer
between 1 and MAXTASK inclusively (the default value for MAXTASK is 16; though,
it can be changed in /etc/system). To load a task only, enter 1 for the
load_option; by default, the loaded task is started once it is loaded.
THE ARTIC CARD MEMORY DUMP
To dump the memory content of an ARTIC card, use the dd command, using as the
input file name dev/ric0, /dev/ric1, etc. The recommended way of viewing the
content of the output file is with the od command, as illustrated by the
example below:
dd of=/dev/ric0 of=/tmp/ric0.out
od -x /tmp/ric0.out | pg
Alternatively, you can open one of these files with a C program and read the
memory content of the card, and then use the printf subroutine to format the
output in octal or hexadecimal.
Processed November 7, 1990 RIC(4,F) 2
RIC(4,F) AIX Technical Reference RIC(4,F)
THE NEW IOCTL SYSTEM CALLS
The syntax for the new ioctl calls is:
ioctl(fd,op,&info)
Before an ioctl call, the following declarations and instructions are written:
int fd;
struct riccmd {
unsigned char task;
unsigned char cmd; /* this structure is actually
unsigned short arglen; defined in ric.h */
unsigned char arg|30|;
} ;
struct riccmd info;
fd = open("/dev/ric00,...);
/* Here the riccmd structure is filled
with the needed information before
the ioctl call is issued */
The ioctl call is issued with the correct file descriptor, the correct ioctl
call, and all the information needed through the riccmd structure. This file
descriptor is used by the driver to get the board number. All the following
ioctl calls are issued to tasks running on the board corresponding to the file
descriptor created with the open call.
The return value of the call is -1 if an error occurs, and 0 if the call is
successful. If the call requests information to be returned from the driver,
this information is stored in the arg array of the riccmd structure used in the
call.
The new ioctl operations are:
ICA_STAT1 This ioctl operation enables a process to read the primary status
byte of a task. The user passes the task number in info.task and
the primary status of that task is returned in the first byte of
info.arg at the end of the ioctl call.
ICA_STAT2 This ioctl operation enables a process to read the secondary
status buffer of a task. The user passes the task number in
info.task and the length of the arg field in info.arglen (the
ARTIC driver uses this value to determine how many bytes to return
without overflowing arg field). The secondary status bytes are
returned in info.arg at the end of the system call. Application
processes should check info.arglen to determine the number of
bytes of data being returned.
Processed November 7, 1990 RIC(4,F) 3
RIC(4,F) AIX Technical Reference RIC(4,F)
ICA_ASYNC This ioctl operation is issued with the task number in info.task.
This call informs the driver of the following:
o The application program wants to be signaled when asynchronous
interrupts come from the task and card specified. The user
does not need to provide the card number because the ARTIC
driver acquires the information through the file descriptor.
o The process issuing the ioctl call handles the signals sent by
the driver when receiving asynchronous interrupt(s) from the
task.
ICA_QURIC This ioctl operation allows callers to determine the number of
ARTIC cards installed on a machine and what their types are. Data
is returned in info.arg and info.arglen specifies the number of
installed ARTIC cards. To determine the length in bytes of
info.arg, multiply info.arglen by three.
ICA_NOASYNC This ioctl operation cancels the effect of the ICA_ASYNC ioctl
operation that was previously issued to the same task on the same
card. The asynchronous interrupts caused by that task are ignored
until another process issues ICA_ASYNC.
ICA_CHKSIG This ioctl operation enables the user to determine the card and
task numbers responsible for generating asynchronous interrupts
after receiving a signal. One or more signals may need to be
processed. The task and card numbers are in the first two bytes
of info.arg respectively at the end of the call.
The program sample below shows how the ioctl operations ICACMD, ICA_STAT1, and
ICA_STAT2 are used:
#include <fcntl.h>
#include <sys/ioctl.h>
#include <ric.h>
#include <stdio.h>
main()
{
int fd, i, rval, nbcopy;
unsigned char b0, b1, b2, b3;
struct riccmd buf; /*defines a structure to be used in
ioctls */
fd = open ("/dev/ric00", 0_RDWR);
/* issue a command to task 3 and check for */
/* error if there is any
buf.cmd = 0xa0; /* command to task 3 */
buf.task=3;
buf.arglen = 0; /* this command has no argument */
if (ioctl (fd,ICACMD, &buf) == -1)
Processed November 7, 1990 RIC(4,F) 4
RIC(4,F) AIX Technical Reference RIC(4,F)
{
printf ("ioctl - ICACMD failed");
goto end;
}
/* look at primary status to check for error */
buf.task=3;
rval = ioctl (fd, ICA_STAT1, &buf);
if (rval == -1)
{
printf ("ioctl (ICA_STAT1) failed");
goto end;
}
/* if there is an error, look at secondary status bytes for */
/* more details */
if (buf.arg [0] == ERROR)
{
/* get the secondary status bytes */
buf.task = 3;
buf.arglen = 6; /* want only 6 bytes */
if (ioctl (fd, ICA_STAT2, &buf) == -1)
{
printf ("ioctl - ICA_STAT2 failed");
goto end;
}
nbcopy = buf.arglen; /* number of bytes ioctl-ICA_STAT2 returned */
printf ("number of bytes returned by ioctl-ICA_STAT2 = %d", nbcopy);
printf ("secondary status byte of task is: ");
b0 = buf.arg [0] ; b1 = buf.arg [1];
b2 = buf.arg[2] ; b3 = buf.arg [3];
printf ("byte" = %x byte1 = %x byte2 = %x byte3 = %x", b0, b1, b2, b3);
}
end: ;
}
THE READX SYSTEM CALLS
A special character file is associated with each ARTIC card. For the first
ARTIC card, there is a corresponding /dev/ric00. For the second card, there is
a corresponding /dev/ric01. Each file has the file protection mode of 0x666,
allowing all users to open these files for reading or writing on ARTIC card
tasks.
Any process can read or write to any task on any board after the correct open
system call has been issued.
To read data from the task input buffer of a task on a particular card, the
user must open the special file associated with the card and issue the readx
Processed November 7, 1990 RIC(4,F) 5
RIC(4,F) AIX Technical Reference RIC(4,F)
command. For example, to read data from the input buffer of a task on card
one, the following sequence of system calls must be issued:
fd = open("/dev/ric01", ...)
readx(fd, buffer, buffer_length, task_number)
An ioctl or writex call must be issued by appl.level to inform the task that
the application level has read the data. The task input buffer can then be
reused by the task. This information is user-defined to establish handshaking
between the application and the tasks.
The readx system call works in the following manner:
Note: The input buffer is a buffer created by a task. The task leaves data in
this buffer for a system unit process to read.
o The device driver gets the input buffer page, its offset and its length
from the interface block on the ARTIC card for the task. If the buffer
provided by the user is shorter than the task input buffer, readx fills the
buffer and updates the file offset pointer accordingly. The next readx,
which is issued to read the input buffer of the same task, copies the data
at the point where the last readx stopped, as long as the file offset
pointer is smaller than the task input buffer length. When the file offset
pointer is equal or greater than the input buffer length, EOF is reached
and -1 is returned. If the buffer provided by the user is longer than the
task input buffer, the buffer is filled with the data from the task input
buffer. File offset pointer is also updated accordingly. The readx call
returns the number of bytes successfully read and a value of -1 in case of
error.
o Although this is a character special file, the lseek system call can be
issued at the application level to change the file offset pointer. This is
done when the application process has exhausted the data in the task input
buffer and it is ready to read another buffer.
SPECIAL CONSIDERATIONS
The C compiler on AIX PS/2 1.2 and the C compiler used to compile ARTIC tasks
have different word alignments, as illustrated in the following example:
struct example {
short int s;
int i;
}
The size of the structure example is 6 bytes for the ARTIC compiler and 8 bytes
for the AIX compiler. In AIX PS/2, the alignment for the integer is doubleword,
which is 4 bytes. The variable s is a short int (2 bytes), which leaves 2
bytes of unused space between s and i.
In the case of the readx system call, the data is copied byte-by-byte from the
input buffer of the ARTIC card task into the user buffer of type structure
Processed November 7, 1990 RIC(4,F) 6
RIC(4,F) AIX Technical Reference RIC(4,F)
struct example. The first 6 bytes of the user buffer contain the data. At the
application level, when the i field of structure struct example is referenced,
only the first two bytes have valid data. The other two bytes of valid data
are in the gap between the variables s and i.
The following diagram illustrates the above:
/* the driver stored the data as followed */
|---------|-----------------------------|
| x | x | x | x | x | x | | |
|---------|---------|-------------------|
/* at the application level, references the data is the following */
|---------|-----------------------------|
| x | x | x | x | x | x | | |
|---------|---------|-------------------|
|short | |int |
|example.s| gap |----example.i------|
To avoid errors caused by alignment problems, all buffers used for the readx
and writex system calls should only be of type char or unsigned char.
DEALING WITH INTERRUPTS (ARTIC CARD --> APPLICATION PROGRAM)
SYNCHRONOUS INTERRUPTS
Each time a command is sent to a task with the ICACMD ioctl system call, the
process sleeps and the ioctl does not return until the ARTIC task interrupts
the process. This interrupt is called a synchronous interrupt.
ASYNCHRONOUS INTERRUPTS
All other interrupts coming from ARTIC tasks are called asynchronous
interrupts. They can be interrupts coming from ARTIC tasks upon completion of
an ICACMDNOWAIT ioctl call (from the task side, these interrupts are
synchronous), or interrupts coming from the ARTIC card that request the
attention of the application program.
PROCESSING THE INTERRUPTS
The device driver processes the interrupts with its second level interrupt
handler ricintr() in the following way:
o In the case of synchronous interrupts, ricintr() wakes up the process
sleeping in the ICACMD ioctl call.
o In the case of asynchronous interrupts, there are two possibilities:
1. Interrupts handled in the kernel by device drivers. For example,
rictty is the device driver in the kernel that drives tty's (terminals
for instance), using the ARTIC task com232.exe to control the transfer
of data through the card.
Processed November 7, 1990 RIC(4,F) 7
RIC(4,F) AIX Technical Reference RIC(4,F)
The kernel subroutine icaintratch arranges a kernel function to be
called when the indicated task on the indicated on the ARTIC card (both
parameters of icaintratch posts an interrupt to the system unit. The
address of this function is put in a structure called taskvec. This
address is checked by the ARTIC card device driver in order to know
which function handles an interrupt coming from an ARTIC task.
struct ricintrvec {
int (*inthandler)();
int intarg;
int taskid;
}
struct ricintrvec taskvec[MAXCYCLONE][MAXTASK+1]
/* max of cards and tasks */
In the rictty thefollowing is an icaintratch function:
icaintratch(0,board,ttytask[unit],rtyintr,0)
/* ttytask[unit] is the task on the board that posts the async. int. */
/* rtyintr is the function called and it is in the taskvec structure */
2. Interrupts handled by the user. If the user decides that he wants to
handle asynchronous interrupts coming from certain tasks, the driver
will then send a signal sigurg to the process that has issued an
ICA_ASYNC ioctl call for the given tasks.
For each ARTIC card, the driver maintains a free list and a queue list. The
free list is a linked list of nodes, twice as long as the number of tasks
allowed to run on the card. Each time a task posts an asynchronous interrupt
and there is a process which has requested to handle the interrupt coming from
this task, the ARTIC card device driver, before sending the SIGURG signal to
the application process, saves the card number unit on which the task is
running, the task number, and the process id number of the process to which the
driver will send the signal. The driver gets a node from the free list, stores
that information in it, and attaches this node to the queue list. Each time a
process issues the ICA_CHKSG ioctl call to find out about the nature of the
interrupt, the driver returns the task number and the card number of the task
that caused the interrupt to the calling process. The unnecessary node is
freed from the queue list and returns to the free list. If more asynchronous
interrupts occur before processes issue ICA_CHKSIG ioctl calls to retrieve
information from the queue list, the free list may be exhausted. In this
situation, the driver reuses the node at the head of the queue. A new node is
attached to the end of the queue.
The following is the structure of the linked list:
Processed November 7, 1990 RIC(4,F) 8
RIC(4,F) AIX Technical Reference RIC(4,F)
struct jobs_queue {
int pid;
unsigned char task_no ;
unsigned char board_no;
struct jobs_queue * next;
}
An opened file is either closed by the last process using it (by issuing a
close system call) or it is closed automatically by the kernel at the end of
the execution of the last process opening the file. This rule also applies to
opened special files. In each case the ricclose function is called by the
kernel. In this module, the driver reinitializes the sig_pend_tb table and
removes the queue to return the space to the free list. These two steps are
performed only for the ARTIC card being closed; other tables remain intact.
SPECIAL CONSIDERATIONS
In the following situation, the user decides to acknowledge asynchronous
interrupts coming from certain tasks. These asynchronous interrupts are sent
when this process is sleeping in a ICACMD ioctl call to a different task
waiting for a synchronous interrupt from this task to wake it up. The
situation is handled in the following manner:
1. Asynchronous interrupts that trigger signals are only processed when the
process is awakened by the synchronous interrupt and returns to the user
mode. Since the tsleep system call is issued with the PCATCH argument, if
sleep is killed by a signal, it returns with the TS_SIG value. The
following loop keeps the process asleep until either the synchronous
interrupt wakes it, or the elapsed time exceeds 6 seconds:
while (tsleep(pid,...|PCATCH,6) = = TS_SIG)
2. If more than one signal is sent to the process while it is sleeping, the
device driver provides information so the user can handle all the
processes. The kernel does not queue the signals when the process is
sleeping but the device driver uses the two lists to keep track of signals
sent during sleep.
The information in the different buffers and primary status of a task
sending multiple asynchronous interrupts during the sleep of the process is
not saved by the kernel. It is up to the user to make his ARTIC tasks in a
way that this transfer of information can be done in correct delays. When
the process is awakened, it returns to user mode and the first signal is
automatically handled in the user defined signal handler. The user then
issues the CHK_SIG ioctl call to find out if other signals were sent to the
process while sleeping. The following provides an example:
Processed November 7, 1990 RIC(4,F) 9
RIC(4,F) AIX Technical Reference RIC(4,F)
handler ()
{
...
while ( ioctl (fd,CHECK_SIGNAL,&buf) != -1)
{
deal_with () /* user defined func. processes the signal */
}
}
3. When the CHK_SIG ioctl command is issued at the application level to
determine if it has any pending signals the driver searches the linked list
for the first occurrence of a node with a process id that is the same as
the process id of the process issuing the system call. If the search
succeeds, the task number and the card number stored in this node are
copied to the user buffer and this node is removed from the linked list.
If the search fails, the ioctl call will return -1.
RELATED INFORMATION
The icaload command in the AIX Operating System Commands Reference.
Processed November 7, 1990 RIC(4,F) 10