• Installing the JDK for FreeBSD • Defining JNI DIO features • Implementing native C data types in Java • Implementing the JNI native interface layer for the DIO interface library The JD
Trang 1if (dio_set_int(s) != 0)
{
printf(“set int failed\n”);
} }
Listing 7-7
The int_handler function parses the user input looking for either the enable or disable string to determine the interrupt setting If the user input
is correct, dio_set_int is called to set the interrupt state
The pciint_handler Function
The pciint_handler function is used to set pci interrupts The pciint
command is entered using the following syntax,
s = enable;
}
else if ((strcmp(state, “disable”) == 0) || (strcmp(state,
Trang 2is correct, dio_set_pciint is called to set the interrupt state
The setpolarity_handler Function
The setpolarity_handler function is used to set the interrupt polarity The set polarity command is entered using the following syntax:
Trang 3The getpolarity_handler Function
The getpolarity_handler function is used to read the interrupt polarity The get polarity command is entered using the following syntax:
Trang 4139 Chapter Seven
Remote Management
The getdirection_handler Function
The getdirection_handler function is used to read the direction of a digital IO line The get direction command is entered using the following syntax:
The quit_handler Function
The quit_handler function is used to exit the shell The quit command
is entered using the following syntax
Trang 5** since this is the program exit we must free the user
** buffer malloced in the main program
The quit command handler releases resources and exits the shell
The help_handler Function
The help_handler function is used to display command help The help
command is entered using the following syntax
The help handler iterates through the command table, displaying the help
string for each command
Trang 6141 Chapter Seven
Remote Management
The DIOShell Utility
The DIOShell main handles all initialization and allocation of resources, then enters the command parsing loop until the quit command is called The utility initialization amounts to the allocation of a command buffer of BUFFER_MAX bytes Once the buffer is successfully allocated, the parser loop is entered
int main(int argc, char *argv)
{
uint8_t* userp = NULL;
if (strncmp(userp, cmdptr->command,
strlen(cmdptr->com-mand)) == 0) {
cmdptr->functionptr(userp, (char *)NULL);
break;
}
Trang 7/* since quit handles cleanup and exit we’ll never get here */ exit(0);
The Makefile
BASE=DIOShell
DEST=/usr/local/bin
INCLUDES=-I / /inc
LIBRARIES=-L /usr/local/lib -ldioif
DIOShell: DIOShell.c Makefile
gcc $(INCLUDES) -o $(BASE) $(LIBRARIES) $(BASE).c
After the discussion of OpenSSH we developed a framework used to
implement a basic command line parser and engine that uses callbacks
to implement actions based on user input This framework is used to
implement the DIOShell, a digital IO specific shell for the DIO Appliance
Trang 88 143
Overview
The next phase of development in the DIO appliance project is to make the DIO interface Web-aware The first step toward this is to develop a Java Native Interface (JNI) for the DIO interface library The JNI is a programming interface, included in the Java Development Kit (JDK) for FreeBSD, for writing Java native methods for calling C/C++ routines
• Installing the JDK for FreeBSD
• Defining JNI DIO features
• Implementing native C data types in Java
• Implementing the JNI native interface layer for the DIO interface library
The JDK
Before we begin developing the JNI layer for the DIO interface library, our development environment must be configured for Java development Configuring the development environment consists of downloading a copy
of the Java Development Kit and setting a few environment variables
Getting the JDK
The first step in developing the JNI layer is obtaining a copy of the JDK The JDK is not a standard item in the FreeBSD distribution but is available in the FreeBSD ports To obtain the latest copy of the JDK, change the working directory to the the JDK ports directory, /usr/ports/java/jdk and execute the following commands:
Trang 9The JDK_HOME Environment Variable
Many of the JDK tools, build environment, and run-time environment depend on the JDK_HOME environment variable to run correctly After building and installing the JDK, you’ll want to set your JDK_HOME environment variable to the directory where the JDK was installed using the make install command On my development system, the JDK is installed in
/usr/local/jdk1.1.8 A quick look at my environment using the setenv command shows my JDK_HOME environment variable set
JDK_HOME=/usr/local/jdk1.1.8
A good place to set the JDK_HOME environment variable is in your startup shell scripts My account uses the csh; the following line is added to my
.cshrc script so it is set at logon
setenv JDK_HOME /usr/local/jdk1.1.8
The CLASSPATH Environment Variable
The CLASSPATH environment variable tells the Java Virtual Machine and other Java applications (for example, the Java tools located in the jdk1.1.8/bin
directory) in which directory to find the class libraries, including user-defined class libraries The DIO class libraries are kept in a separate directory in
/usr/local/dio, the directory used to keep the DIO software
# setenv CLASSPATH=/usr/local/dio/class
The CLASSPATH variable is set in the login script
Trang 10145 Chapter Eight
JNI Layer
The LD_LIBRARY_PATH Environment Variable
LD_LIBRARY_PATH is an environment variable where the system’s library loader looks for shared libraries before looking in the system’s default shared-library directories The default shared-library search path is /lib, /usr/lib, /usr/local/lib
shared-# setenv LD_LIBRARY_PATH=/usr/local/dio/lib
As with the JDK_HOME and CLASSPATH environment variables, the
LD_LIBRARY_PATH variable is set in login startup scripts
Creating the JNI Layer
In this section we are going to develop a JNI layer to read and write digital
IO lines and read the configured direction of a line Since we’ve already developed a C interface to perform these tasks, an intermediate layer is developed, the JNI, which provides the mechanism to call C code from Java code Before we turn the crank and begin emitting mass quantities of Java source code, let’s take a look at the procedure for developing our JNI interface First, we need to identify the features that are going to
be called from Java As previously mentioned, they are
read line, write line, and read direction These three
capabilities have been implemented by the DIO interface
library as dio_set_line, dio_get_line, and
dio_read_direction In addition to the functions,
we are going to implement the enums used by these C
functions Java doesn’t support enums as a native type,
so we’ll need to be a little creative
With the features defined, we’ll take the first step in
developing the JNI layer—defining a Java interface for
the DIO interface library functions The Java interface is
a Java class that uses native functions as the bridge to
call our C DIO interface library The Java functions call
the native functions, which, in turn, call the C functions Figure 8-1
Java
The JNI Layer
C Interface
The JNI Layer
After the Java class is defined, the Java compiler, javac, is run, which produces the java class files Java class files are byte codes typically executed by the Java environment
Trang 11The Java class files are used as
input to another tool in the
JDK, javah Javah produces a
native header file for functions
that were declared native in the
Java class The generated header
file is used for inclusion in the
C file that implements the
native functions
Finally, once the Java object is
completed, the native imple
mentation is completed, and
the code is compiled into class
files and shared libraries and
located in the appropriate
directories pointed to by the
LD_LIBRARY_PATH and
CLASSPATH environment
variables, so it can be executed
Java Native Class
Figure 8-2 Structure of JNI Layer
Implementing C enums in Java
Even though Java has borrowed heavily from C++ syntax, some features of the C++ language are not implemented in Java One such feature is enums Since the dio_set_line, dio_get_line and dio_read_direction
functions all use enums, we are going to define corresponding types in Java There have been a few slightly different definitions of enums in the C language Initially they were just integers with no type checking In later definitions, they became proper types but with minimal type algebra For clarity, I’ll define
my intended use of enums, and we’ll proceed with the Java implementation
A C language enum is a list of objects grouped together into a fixed set Each
of the objects within the list have a defined value
In Java, we can create the grouping using a class with a private constructor
By making the constructor private, the creation of class objects is restricted Because we’ve made a class with a private constructor, creation of new objects is limited to that class We can now create the members of the enum within that class Let’s take a look at the DIOLineState class
Trang 12147 Chapter Eight
JNI Layer
The DIOLineState Class
The C implementation of the line state is an enum with two states, clear and set Let’s take another look at the diolinestate_t, defined in dioif.h in Listing 8-1
typedef enum diolinestate_t
The diolinestate_t enum contains two members, clear with a value of
0 and set with a value of 1
The DIOLineState class in Figure 8-2 provides an implementation of two states scoped by DIOLineState The DIOLineState constructor is private, limiting the instantiation of DIOLineState object to class members In essence we’ve created a name scope, DIOLineState, similar to the C name scope diolinestate_t Now that we’re resolved the naming scope, we have the remaining task of defining the line states Both set and clear are defined as public static final members, making them publicly accessible fixed values of the DIOLineState class
public final class DIOLineState
Trang 13Using the DIOLineState class defined in Figure 8-2, we’ve created two states scoped by the DIOLineState name that can be used by Java programs,
DIOLineState::set and DIOLineState::clear
The DIOLineNumber Class
As with the DIOLineState class, the DIOLineNumber class uses a private constructor to limit the scope of the member functions for the
DIOLineNumber class The DIOLineNumber class contains a static final method for each of the enum values in the C enum, effectively emulating the
C enum values Listing 8-3 contains a complete listing of the
{
return(number_);
};
Trang 14The DIOLineDirection Class
The DIOLineDirection class implementation follows the same pattern as the DIOLineNumber and DIOLineState classes defined in the previous sections The class contains two public declarations that represent the values of the line state These values, lineout and linein, directly correspond to the C enum
dio_direction, defined in the DIO Interface library developed in Chapter
5 Listing 8-4 shows our implementation of the DIOLineDirections class
public final class DIOLineDirection
Trang 15The implementation of the DIOLineDirection class contains a private constructor limiting the values of the class, and a private variable, direction_, that contains the value of direction, which will be used to pass into the C implementation of the DIO interface
The Java Code
Now that some of our basic data types have been defined, it’s time to define the Java-to-C interface class Perhaps the most important task in defining the JNI class is to first define which features are included For simplicity’s sake,
we will define a Java class that provides the same capabilities provided by the diod daemon, a JNI layer that provides member functions to read, write, and determine the direction of the PCI-DIO24 digital IO lines The interfaces for each of these functions are contained in the DIO interface library; the JNI provides a way to call these functions from Java
Developing the JNI layer requires nothing more than the use of the native attribute keyword The native keyword allows the implementation of the member function to be deferred The actual implementation of the native member functions will occur in a C++ class, defined in a later step
The DIOIfJNI Class
Let’s take a look at the DIOIfJNI class The DIOIfJNI class is used to create a bridge between the Java world and the DIO interface library The first task is
to import the classes we used to implement the DIO interface enums in Java,
DIOLineNumber, DIOLineState, and DIOLineDirection
The Java implementation of each of our functions—read line, write line, and read direction—are similar; they call a native function that calls the DIO interface library and returns any data obtained from the DIO interface library back to the calling function Listing 8-5 contains a subset interface to the DIO interface library and the contents of the file DIOIfJNI.java
Trang 16151 Chapter Eight
JNI Layer
public native int SetLineNative(int line, int value);
be used to the corresponding C function, GetLineNative, SetLineNative, and GetDirectionNative
Trang 17Generate the Class Files
With the initial Java class defined, the class needs to be compiled using the Java compiler, javac The output of the Java compiler, the Java class file, is necessary for the next step, so we’ll be unable to proceed without compiling The Java class is compiled using the following command:
# javac DIOIfJNI.java
The output of this command will be the file DIOIfJNI.class The byte codes output from the javac compiler represent the implementation of the DIOIfJNI class When working with native methods, the bytecodes are also used to generate a header file for the native code to be developed We’ll learn more about this in the next step of developing the JNI layer
Generate the Header Files
The Java class file is used as input for the javah tool The javah tool uses the
java.class file and generates ANSI function prototypes for the defined native member functions It is important that the class files reside in a directory contained in the CLASSPATH environment variable
# javah –jni DIOIfJNI
The –jni option in the example tells javah to generate a JNI function prototype header The output of the javah tool is the file DIOIfJNI.H; Listing 8-6 contains the complete listing of this file DIOIfJNI.h is available for inclusion by the file containing the native source code implementation
* Class: DIOIfJNI
* Method: SetLineNative
* Signature: (II)I
Trang 18153 Chapter Eight
JNI Layer
JNIEXPORT jint JNICALL Java_DIOIfJNI_SetLineNative
(JNIEnv *, jobject, jint, jint);
/*
* Class: DIOIfJNI
* Method: GetLineNative
* Signature: (I)I
JNIEXPORT jint JNICALL Java_DIOIfJNI_GetLineNative
(JNIEnv *, jobject, jint);
/*
* Class: DIOIfJNI
* Method: GetDirectionNative
* Signature: (I)I
JNIEXPORT jint JNICALL Java_DIOIfJNI_GetDirectionNative
(JNIEnv *, jobject, jint);
One other important note is that the generated header files include the file
jni.h This file contains all the necessary JNI definitions It is important to point the include directory path for the C compiler to the JDK header files We’ll look at this in more detail in the Makefile section of this chapter
The Native Code
Now it’s time to write the implementations of the native functions The native functions represent the bridge from Java to C Native functions are written using C++ and are called by the Java member functions in the DIOIfJNI object These functions, in turn, call directly through to the C implementation of the DIO interface library The primary object of the native functions is to provide the glue to massage Java data types onto C data