Microsoft word - device_drivers_in_java_paper_from_qnx.doc

Device Drivers and System-Level
Programming in Java

Chris McKillopSoftware EngineerQNX Software Systems Ltd.
[email protected] Introduction
In the not-too-distant past, engineers who wrote device drivers, operating systems, and other system-level software did most of their work in assembly. Today, those same engineers use C — even thoughhand-tuned assembly can still run faster and smaller than the code generated by many C compilers. Sowhy the change? Because experience has shown that the portability, speed of development, and built-in runtime support (the C Standard Library) of C far outweigh the relatively small cost of using it. Asit turns out, much the same can be said for Java. In fact, system-level software written in Java can offeran even better cost-benefit ratio, making Java a viable language for both current and future embeddeddesigns. In this paper, we walk through the steps of writing a device driver in Java, from hardwareinitialization to client interfacing. We also address key issues, such as realtime performance andportability, and identify which types of applications benefit the most from Java device drivers.
Driving Hardware
What, exactly, is system-level programming? For most developers, it means programming at the lowestlevel in the system, talking directly to, and controlling, hardware. Device drivers represent a specific typeof programming at this level. Typically, they interface with both the operating system (OS) and thehardware to give application-level programs access to devices in some standard fashion. (An upfrontwarning: some types of device drivers can’t be done in Java. For example, under many OSs, driversare either loaded as shared objects or statically linked with the kernel itself. You couldn’t use Javato write a device driver for such an OS since the Java Virtual Machine runs outside of the kernel.) System-level Programming in Java • 1
Hardware initialization — To “drive” the hardware means talking to it, which in turn requires
direct access to it, through either mapped memory addresses or specific in/out instructions.
Interrupt handling — To set up an interrupt handler, the driver may need to talk to CPU-specific
hardware. In most cases, however, it interfaces with interrupt services provided by the OS. Usingthose services, the driver can register an interrupt handler that is invoked or unblocked wheneverthe desired interrupt fires.
Client interfacing — Interfacing with clients is the trickiest part, and varies from system to system.
On many systems, the client has to make system calls to the kernel, requesting to interact with thespecific device. Thankfully, that isn’t the only way to interact with clients.
Before exploring these three areas in detail, we need to cover some key Java concepts — in particular,the Java Native Interface, or JNI.
The Java Standard Library provides a lot of functionality. However, it doesn’t always provide thefeatures needed to interface directly to the underlying system. Consequently, Sun introduced the JNI,which lets you safely and easily call native functions loaded from a shared library. These functions canbe passed classes as parameters, interact with those classes, throw exceptions, and behave (from theperspective of the Java program calling the native function) just like any other Java function. Tobecome a native method, a function within a class simply uses the keyword “native” coupled with astatic declaration. For example: The native method test() can now be invoked as NativeTest.test(). In response, the Java Virtual Machine(JVM) will to try to find the symbol Java_com_qnx_examples_NativeTest_test in its process space. Asyou can imagine, however, running the above example alone will cause an exception (specifically, ajava.lang.UnsatisfiedLinkError), as it is unlikely that any of the default shared libraries loaded by theJVM will provide that symbol. Consider, therefore, the following example: System-level Programming in Java • 2
The static section added to the NativeTest class is invoked before any other static functions. TheSystem.loadLibrary() call, meanwhile, tells the JVM to load the mynative library. This library is systemdependent — on UNIX, for instance, the JVM would load; on Windows, mynative.dll. Inorder for test() to function, the mynative library must include at least the following function: Java_com_qnx_examples_NativeTest_test(JNIEnv *env, jclass obj) Every JNI function takes at least two parameters: a pointer to the JVM’s environment in order to interactwith it, and a reference to the instance of the class that is invoking the JNI. Since the test() function itselftakes no parameters, only those two parameters need to be defined.
If the above source is compiled into a shared library for your system, named appropriately, and placed ina location where the JVM can find it, then NativeTest.test() will run and return –1. The remaining macros(JNIEXPORT, JNICALL, …), which are provided by the jni.h header file, hide JVM specifics (forinstance, calling conventions) as much as possible, allowing your JNI code to be more portable.
Hardware Initialization: Accessing Physical Memory
Though the ability to extend the JVM with native methods is a powerful feature, there is a drawback:every time you use a native method, you have to “port” that method when moving to a new system. Agood design will keep logic and high-level work in pure Java, with the minimum JNI code required toget the work done. Such an approach allows for maximum re-use of natives between projects.
For example, consider the following class, which serves as a container for POSIX system calls. Bykeeping all of your natives in a single class like this, you can greatly simplify porting between systems: public static native long mmap64(long addr, long len, public static native int munmap(long addr, long len); public static native byte in8(long addr); public static native void out8(long addr, byte value); Using this POSIX class as a base, let’s examine how a driver can access hardware for configuration andcontrol. As mentioned earlier, hardware initialization requires gaining access to the hardware. If your System-level Programming in Java • 3
JVM complies with JSR-0001, the Real-time Specification For Java (RTSJ), you can do this easily, usingthe RawMemoryAccess class that the RTSJ defines. Instances of the RawMemoryAccess class allowyour Java driver to map a specific physical range of memory and then read or write values into thatmemory area. The driver can, as a result, initialize and control hardware devices.
At the time of writing, only one JVM (jTime from TimeSys) fully implements the RTSJ specification,while another (j9 from IBM) implements portions of it. By using the POSIX class, however, you canbuild a simple class that works much like RawMemoryAccess; in fact, this class will allow your code towork regardless of whether your JVM supports the RTSJ. For an example of such a class, see the codesample below.
Note that the RTJS supports several other access methods, including get/setShort() and get/setInt(), aswell as setBytes(), which sets a range of device memory to a given range of byte values. You can addthese functions as needed while maintaining portability with a system that has a completeRawMemoryAccess class.
public RawMemoryAccess(Object type, long base, long size) { public void setByte(long offset, byte value) { System-level Programming in Java • 4
Interrupt handling
Compared to memory access, which has a very simple interface, interrupt handling is fairly complex.
For starters, some OSs won’t let even let you access interrupts. Windows and Linux, for instance,allow only kernel entities to access the interrupt subsystem. In comparison, OSs targeting embeddedand real-time markets either make no distinction between application and kernel space (e.g. VxWorks)or provide a suitable API for dealing with user-space interrupts (e.g. QNX® Neutrino® RTOS).
Even if an OS provides an API that can be accessed via the JNI, you may still have to address timingissues within the JVM. If the JVM conforms to the RTSJ, no problem: you can create real-time threadsthat run unencumbered by the JVM garbage collector. Without those real-time threads, however, anyinterrupts managed in Java will be at the mercy of the garbage collector and must, as a result, toleratelatencies that last milliseconds or even seconds. If such latencies are unacceptable, then a solutionthat leans on the JNI is in order.
Assuming that a pure Java solution is feasible, you could build a JNI container class for interruptmanagement, as shown in the following code example. Using such a class, you can start a high-prioritythread (or NoHeapRealtimeThread if the RTJS available) that would attach to the required interrupt,unmask the interrupt, and wait for the interrupt in a processing loop. The thread could then read orwrite to the hardware on each interrupt and, using a thread-safe data structure, publish any data tolower-priority threads in the system.
public static native int InterruptAttach(int irq); public static native int InterruptDetach(int id); public static native int InterruptWait(int id); public static native int InterruptMask(int id); public static native int InterruptUnmask(int id); For some systems, this almost pure Java solution won’t provide the desired performance and latency. If so,you must use a native interrupt handler. During the RTOS.InterruptAttach() call, the associated JNI codewould create a thread or register a handler function, then return a pointer to a heap-allocated data structurefor the ID. Instead of calling RTOS.InterruptWait(), the code would call RTOS.InterruptPopData() andRTOS.InterruptPushData(). The native thread would then read and write from FIFO-style (first in, firstout) structures as interrupts fire, unblocking any Java threads that are waiting for data to “pop.” SeeFigure 1.
System-level Programming in Java • 5
Native Code
Figure 1 — Using a native interrupt handler.
Client Interfaces
A device driver is of little use if the data it moves around can be accessed only by the driver itself — thedriver needs a way to communicate with other parts of the system. On traditional OSs such as UNIX, aclient process can access a device by opening a file descriptor to the device — usually by calling open()on something in /dev. When the process subsequently makes a system call that passes this descriptor, thekernel routes the call to the proper device. Obviously, this sort of approach won’t work for a driverwritten in Java. On systems such as the QNX Neutrino RTOS, on the other hand, it’s possible for anyuser-space application, including programs running in a JVM, to register a device and handle the open/read/write actions. Still, this approach isn’t always portable. That leaves us with two possible routes fortalking to clients — sockets and inner-JVM.
Java was built for “The Network.” Any compliant JVM (J2ME and up) can act as a TCP (or UDP)server. This provides an obvious choice for client programming, particularly since most other languagescan also talk over TCP/IP. By having a thread in your Java driver act as a TCP daemon on a specific port(or range of ports), you allow the driver to be accessed by any client process on the system (or thenetwork, if needed). Since sockets are, on most systems, file descriptors, you can even use standardfunctions like read() and write() to communicate with your driver from C programs.
System-level Programming in Java • 6
If your entire system is being written in Java, you can provide an interface to your driver that isaccessible through Java only. To do this, you could provide facilities to get an InputStream and/or anOutputStream to your driver for performing I/O. Or, you could simply define a standard IDriver interfacethat all clients would use for talking to any Java driver in the system. The upside to this approach isperformance: it bypasses the entire network stack and minimizes data copies. The downside is fragility:if your JNI code or the JVM causes a fault, then your application and your drivers will be taken down allat once.
What’s This Good for Anyway?
Obviously, some devices are better suited to system-level work in Java than others. Front-panel/HMI systems, for example, are ideal candidates, particularly since buttons, touch-screens, and videooutput drivers are easily coupled with application logic. Also, by using Java’s extensive built-in GUIAPIs, you can set up an emulation environment that runs with a simulated frame buffer in a window,with widgets for the buttons. Such an environment lets you design, build, and debug the application-driver interface even before you have hardware in-house.
Other candidates include any low-interrupt rate, DMA-capable device. Such devices have enoughbuffering between interrupts for the latency present in the JVM to be a minor issue. Poor candidatesfor Java drivers include high-interrupt devices with small hardware buffers and low-latency demands— 16550 UARTs and IEEE 1394 adapters, for instance.
Overall, Java has become a tool that embedded designers should consider when building new systems.
Compared to C, it offers greater portability, a higher level of design abstraction, and a richer standardlibrary. Moreover, its performance is starting to rival that of many C compilers. And, as we have shown,Java can serve as the basis not only for applications, but for device driver and other system-level work aswell.
2004 QNX Software Systems Ltd. QNX, Momentics, and Neutrino are registered trademarks of QNX Software Systems Ltd. incertain jurisdictions. All other trademarks and trade names belong to their respective owners. System-level Programming in Java • 7


05 quinto vol -1965

05 quinto vol -1965 25-01-2008 13:55 Pagina 305 quinto vol -1965 25-01-2008 13:55 Pagina 171scusa se non rispondo tempestivamente alle tue lettere, masono sempre in un mare di guai. La doppia vita, federalista e pro-fessionale, non concede tregue. In ogni modo seguo con grandeinteresse la tua esperienza americana, e per così dire vorrei dar-tene la prova. I due articoli che mi hai inviato mi s

CLINICAL Treating actinic keratoses PRACTICE with imiquimod • There are many options for managing actinic Case study keratoses. Percentage complete clearance of lesions Anthony Dixon with cryotherapy is in the order of 80%, topical imiquimod 70% and 5 fluorouracil 50%.2–5• Some patients will find some of the options more tolerable tha

© 2010-2017 Pdf Pills Composition