Begin File: iomanager.tgz Description: I/O manager implementation under QNX 4 Keywords: manager Manager I/O iomanager Version: Entered-date: 05/01/99 Author: Ported-by: Original-site: Copying-policy: CMU Supplemental: This I/O Manager framework is a sample retrieved from the QNX/4 Realtime Programming Course. It is modelled after the resource manager discussion in Chapter 6 of the "Getting Started with QNX4" book written by Rob Krten. Ask your QSSL sales contact about this book if you do not already have one. This I/O Manager framework is designed to present an easy way to get started in writing POSIX conforming interfaces. The POSIX interface is made up of many calls (ie. read / write / seek / ioctl...). This software is provided by QSSL "as is" and is free to anyone wishing to use it in the development of their own I/O Manager. This I/O Manager framework software is NOT a QSSL supported product. It has been written as a template to help standardize and hopefully simplify the development of I/O Managers. This framework presents a POSIX interface for 21 _IO_* messages plus some additional _FSYS_* and _SYSMSG_* messages. POSIX messages should always be handled properly by all I/O managers, not just ignored. The framework provided here does just that, it provides stub functions that support all the known and expected messages for the POSIX interface. You as the programmer, need only create those functions which you wish to implement within your I/O manager and have these functions override the default stubs provided by the framework. Why would you want to use this framework for an I/O Manager? There are many reasons but here are a few highlighted ones: a) provide a POSIX interface to applications using this I/O Manager b) can use standard QNX apps to test (cat , echo ...) c) detect when a client process receives a signal and act accordingly d) regardless of how a client process exits, the qnx os will supply close calls for files that are not already closed (thus your application will always receive a close call from its clients. e) allows concurrent development of applications and I/O Manager due to common POSIX interface for testing. Can test either component against the POSIX API rather than each other. f) and more... The following text in this document has two purposes: 1. Describe how this I/O manager framework expects to function at runtime, 2. Describe how one might design their own I/O manager with respect to the existing framework using an example of a data acquisition system, This I/O Manager framework consists of the following directories: ./iomgr - root of i/o manager ./iomgr/lib - library of i/o manager framework ./iomgr/example - directory containing several examples using the i/o manager ./iomgr/daq - a simulated data acquisition i/o manager implementation. Described later in this document. Change to directory ./iomgr/lib and run 'make' to build the i/o manager framework library ./iomgr/lib/iomgr Change to directory ./iomgr/example and run 'make' to build the 'pipe', 'volt' and 'iorecv' examples. Use the 'writeread' and 'reader' to facilitate testing. Change to directory ./iomgr/daq and run 'make' to build the daq example that is described later in this document. 1.0 I/O Manager Framework A typical I/O Manager consists of two components: first, there is the component that manages a resource such as a data acquisition card, and second, there is the component that manages the interface between the resource and applications that wish to access or control the resource. Obviously, due to the numerous and various types of resources, coupled with the implementation requirements of your own specific resource, this framework leaves the majority of the development for this "device specific" work to you. It is the second component, the management of the POSIX API, plus some glue to bind the two components together that is the focus of this framework software. The following description of the I/O Manager Framework described here is at a high level. For detailed understanding of how it is implemented please run and test the supplied example implementations and browse the source files. Remember, it is all sample code and free for you to use and modify as you see fit. The I/O Manager framework provides a POSIX interface using the QNX4 IPC Message passing architecture in a single threaded model. The I/O Manager will perform the following: - register your I/O manager with a pathname space, - handle the main receive loop, - perform most of the work for the I/O messages, - perform work for some system and filesystem messages, - call your provided functions in place of default functions when these functions are made available, The I/O Manager framework also provides some utility functions which you may make use of within your own routines for operations such as reading and writing large messages between the I/O manager and an application. All of the software for this framework is provided in the iolib directory. The Makefile provided will compile this source and create an iolib library for you to link into your program. There is an additional piece of software that is provided, the main() function. The default main() function is provided in the example directory. You can use it as a initial building block. There is more on this function in the example later in this document. The I/O Manager framework utilizes dynamic data structures for managing devices and I/O requests. The three data structures which are of interest to developers implementing this framework are: 1. io_funs - callback table for user provided functions 2. inode_t - device management structure (1 per device) 3. file_t - file management structure (1 per open device) These three structures are defined in iomgr.h in the iolib directory. The inode_t and file_t structures provide, in addition to their device and file specific data items, access points for user defined data. The inode_t.ino_data data item can be used by you to contain your own device specific information for each defined device. As well, the file_t.f_data item is an array of 4 unsigned longs which may be used to retain some session information on an opened device. The POSIX calls supported by this framework are managed through a set of functions which are invoked in a callback fashion. The main receive loop is in the server.c file. It waits on incoming messages then calls the appropriate function to service the new message. You need to populate those fields in the io_funs callback table with pointers to your own functions to support the POSIX calls that you want respond to. These functions called in the callback fashion are provided the appropriate context specific data structures for the specific POSIX interface. Depending on the type of information being received from the POSIX interface, you may need to use the QNX IPC functions to read additional data from the receive message. Again, the information needed to determine this is provided as input parameters to the callback functions. This will be illustrated more in the example I/O Manager description provided later in this document. Because this I/O Manager supports the POSIX interface it should also set the errno global variable before returning any errors. Any code that you write to support a POSIX call should provide this feature. 2.0 Designing with an I/O Manager Framework There are many ways in which you can design your own I/O manager. The example I/O Manager described here is for a simple data acquisition resource. This daq I/O Manager consists of the following files: daq/close.c - Responds to POSIX close daq/create.c - Contains create_dev function which is the function provided by you that will create the devices. It must be called create_dev. daq/hint.c - H/W Interrupt handler daq/killed.c - Responds to detection of client being signalled daq/main.c - main (part of I/O Manager framework). daq/proxy.c - Responds to proxy generated from hint. daq/qioctl.c - Responds to qnx_qioctl daq/queue.c - Managers queues in daq I/O Manager daq/read.c - Responds to POSIX read daq/daq.h - DAQ internal header daq/daq_if.h - DAQ I/F header (used with qioctl.c) iolib/iomgr - I/O Manager framework library daq/testdaq.c - Test program used to test daq I/O Manager To help the readability of the code in this I/O Manager example some notation standards have been implemented. The following prefixes are used in the user generated code for daq: DQ - Global variable or function dq - data type definitions in daq.h - local variables, static variables, - local functions The daq i/o manager will allow, via command line input, the user to start the I/O manager and associate it to a certain QNX name space. ie. > daq -v -p /testdaq a b c The above example will: a) start up the I/O Manager "daq", b) set the "verbose" global variable using "-v" c) create the pathname "/testdaq" (verify using the prefix command) d) create the subpaths "/testdaq/a", "/testdaq/b", & "/testdaq/c". Once started, the daq i/o manager will respond to open, read, write , close and other POSIX calls. In addition, the qnx_ioctl call is also supported. The I/O Manager frame work will set the initial priority of this program to priority 22. The timer hardware interrupt is used to simulate the generation of data on a data acquisition card. The data simulation rate can be varied by altering the system ticksize. Due to some privileged function calls in the I/O Manager such as qnx_hint_attach and Trace, the daq I/O Manager needs to be run as root. To do this, we have set the "s" bit on the daq program in the Makefile using chmod. As well, the interrupt handler needs the -T1 compile option. The daq device specific data structures are defined in the daq.h header file. The main daq device structure is daqDev. Run-Time Description The main program performs a fork. The child process will become the I/O manager and the parent process will wait for the I/O Manager to come up then create device a, b & c. Once all the required devices are created the parent process will exit. In this daq example, a global array (irProxyList) is created to provide synchronization of the devices between I/O requests and the interrupt and related proxy requests. All relevant I/O manager data can be found via this global data structure. Thus, your I/O Manager has two ways to acquire the device information data; either via the inode_t data structure provided as an input to a callback call or the irProxyList global array. The data acquisition component is handled by the hint.c and proxy.c modules. The hint.c contains the hardware interrupt handler. It will generate the data and place it into one of the device structures read buffers. The read buffers are implemented as a swing buffer couple where the hardware interrupt will fill one buffer, trigger a proxy to have the I/O manager read the buffer while the hardware interrupt handler fills the other. The data read from the swing buffer will either be sent to a process that is currently blocked on a read of this device, or queued to a double linked list queue associated with the device daqDev.inputQ. There has been a #define limit put on this inputQ of 20 512byte buffers. Anything beyond this is discarded. This daq I/O Manager will continuously fill the inputQ from data generated by the hardware interrupt. Applications such as cat or the testdaq program also supplied here can then be run open and read from the device files /daq/a, /daq/b and /daq/c to simulate real data acquisition applications. This daq I/O manager will issue the qnx_flags to catch the signals sent to clients that are currently blocked on reads or whatever with this I/O Manager. The proc_killed (killed.c) function reacts to client signals. With the implementation of the swing buffers the hardware interrupt handler execution time is kept low. The proxy handler will perform the time consuming tasks associated with the interrupts but at the process priority rather than at the interrupt priority. The POSIX read is support via the read.c file. A read request will pull data from the inputQ (if there is some) to the requester's read buffer. If the request is for an amount of data that is greater than what is available in the inputQ then the request will be filled as much as possible and then added to a procWait queue that is associated with the device. The proxy callback will then fill the remainder of the request data from the incoming swing buffer as the data becomes available. The qnx_ioctl QNX function can be used to set or obtain device specific information. Currently it does little, it is only here to present it as a means of allowing an application to set/retrieve device parameters. An Example: Extract this tar file. This readme file is iomgr/README.DAQ > tar -xvf daq_iomgr.tar > cd iomgr/iolib > make > cd ../daq > make all Change permissions of files if not already done. > chown root daq > chmod u+s daq Startup the daq io manager in verbose mode. > daq -v -p/testdaq a b c Should see something similar to the following: Creating path: /testdaq/a Proxy Id is 26282 Installing hardware interrupt Close any open filedes for process 26265 Creating path: /testdaq/b Proxy Id is 25259 Close any open filedes for process 26265 Creating path: /testdaq/c Proxy Id is 26285 Close any open filedes for process 26265 Verify the pathnames created > prefix Should see an entry for /testdaq Verify the permissions of the devices that were created > ls -l /testdaq Should see something similar to the following total 0 crwxrwxr-x 2 root eng 14, 0 Dec 23 16:45 a crwxrwxr-x 2 root eng 14, 1 Dec 23 16:45 b crwxrwxr-x 2 root eng 14, 2 Dec 23 16:45 c Change permission of /testdaq/a for the testdaq program to run properly. > chmod 777 /testdaq/a Run the test program testdaq > testdaq Should see output similar to the following: Process Id: 25382 is requesting a read of 2 bytes %& Process Id: 25382 is requesting a read of 2 bytes '( Process Id: 25382 is requesting a read of 2 bytes )* Process Id: 25382 is requesting a read of 512 bytes +,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz {|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijk lmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\ ]^_`abcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLM NOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./0123456789:;<=> ?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !"#$%&'()*+,-./ 0123456789:;<=>?@ABCD Process Id: 25382 is requesting a qioctl of 4 bytes qnx_ioctl's return value is: 0 Close any open filedes for process 25611 Try using other QNX utilities to read from the devices created here. > cat /testdaq/c > hd < /testdaq/b > spatch /testdaq/a End