Tales from the Trenches
Included on "Byte Magazine, Nov 1990"
TALES FROM THE TRENCHES
In January of 1989, I was giving a presentation on OS/2 to representatives of a prospective client. They had a DOS-based system for data acquisition that lacked the ability to simultaneously gather and process data. They had done their homework and concluded that OS/2 could do the job, but they weren't convinced that Unix could not do the job as well.
It was a perfect application for OS/2. The system had to monitor serial-bus transactions and voltage level in real time, and it had to act on certain conditions immediately. This quickly ruled out Unix, which lacks a preemptive, everything was going well until one of the senior engineers asked an obvious question: "Of course, we can get device drivers for our special hardware, right?" I did some handwaving and went on to other topics, promising I'd get back to them regarding the drivers.
When I called the various hardware vendors, I got the same answer every time. "Sorry, we only have DOS drivers. We'd like to support OS/2, but we don't have anyone who knows how to write them. We understand they are extremely hard to write, and only a few customers have asked for them anyway." I decided to find out why. Why should writing a device driver for OS/2 be so difficult?
I packed my bags and headed out to Microsoft University for the OS/2 Driver Writing course. The class ran for one week, and it was one of the most intense I have ever attended.
When an OS/2 application needs to perform I/O, it makes an I/O request call to the kernel. The kernel verifies the request,translates it into a driver request packet, and calls the device driver for service. The driver handles all the hardware details: I/O addressing, timing, register setup, interrupt handling, and error checking. When the device responds, the driver massages the data into a format recognizable by the application, sends back the data (or a status message), and notifies the kernel that the request is complete.
If it can't handle the request right away, the driver may either block the requesting thread or return a "request not done" to the kernel. Either way, the driver relinquishes the CPU and lets other threads run. If there's an error, the driver relays it to the kernel along with a "request complete" status.
What makes OS/2 drivers unique is the need to operate in both real mode and protected mode. Addresses computed in real mode are not valid if the system switched to protected mode, and vice versa. The driver has to handle such mode switches on the fly. Understanding this bimodal operation is the key to writing OS/2 1.x drivers. Several Device Helper (DevHlp) routines support bimodal operation, but learning how to organize them properly can be harrowing.
Jumping into the Deep End
When I got back from Microsoft University, I was anxious to plunge into my first driver. I ordered the device-driver development kit (DDK) from Microsoft, which comes with the all-important kernel debugger. KDB is a replacement kernel that, among other things, has knowledge of driver structures. For instance, to display a request packet, you can use the command .d req es:bx. KDB formats the data and displays it in request packet form. Don't even think about writing an OS/2 driver without this tool!
I began with a simple, do-nothing driver based on examples given in the course. It worked perfectly. Next, I tackled the real project. My client needed a driver for an eight-channel A/D board. The board used an intelligent interrupt-driven controller and could do DMA transfers. I fumbled furiously through my student documentation for examples of how to implement such a driver and broke out in a cold sweat. There were no examples of interrupt handlers, no examples of DMA operation, and no examples of user-defined I/O control functions.
Microsoft, when I called for help, referred me to Compaq (I'm using its version of OS/2). Compaq referred me back to Microsoft. I searched the compute bookstores to no avail. Finally, I just rolled up my sleeves and began to experiment.
The driver's job is simple - in principle. It has to manage requests from the kernel and return results to the application. An OS/2 driver receives two kinds of requests: Some can be completed immediately, and some can't. Requests come in by way of a standard data structure called a request packet. The kernel sends the driver a bimodal pointer to the request packet. Since the driver must operate in real mode or protected mode, the bimodal pointer ensures that the request packet will be accessible in either mode.
When a request can't be handled right away (e.g., in the case of a disk seek), the driver (by means of a set of DevHlp routines) places it in a queue. Disk drivers can choose to sort pending requests for disk seeks in sector order, to minimize seek time.
OS/2's threaded architecture assigns one extra responsibility to the device driver. When a driver can't handle a request right away, it blocks the requesting thread; when it completes the request, it unblocks the thread.
Tools for Driver Development
The DDK comes with a three-ring binder containing driver structures, descriptions of the DevHlp routines, and instructions for using the KDB. I found only the first 40 or so pages useful. The book does describe the DevHlp routines in detail, but it contains no examples of working drivers.
I write all my device drivers, including interrupt handlers, in Microsoft C 6.0 with maximum optimization. Don't waste your time writing your driver in assembly. Writing a device driver in C takes about half the time it would take to write the same driver in assembly, and the driver will work just as well.
Another useful tool is DDC.LIB, which is a C-callable device-driver library from PentaSoft (17541 Stone Ave. N, Seattle, WA 98133, (206) 546-0470). Probably the most important function in DDC.LIB is Transfer, which transfers data between the driver and applications and accounts for mode switching during the transfer. It handles transfer of data from virtual memory to physical memory, physical to virtual, virtual to virtual, and physical to physical. If you're serious about OS/2 driver development, this library is a must.
Light at the End of the Tunnel?
Anyone who has written drivers for other multitasking operating systems (e.g., Unix or VMS) will have a good foundation for OS/2 driver development. Microsoft estimates that it takes an experienced C programmer who has attended the Microsoft University OS/2 Driver Writing course four to six months to write his or her first OS/2 driver. Subsequent drivers should take two to four months. Disk drivers are significantly more complex and may take longer.
My first driver took roughly three months to write. The next one took only two months, and I was able to write a few simple drivers in a week or so, so it does get easier with practice.
Although OS/2 device drivers are becoming more common nowadays, the situation remains farly grim. Most of them are for specialized hardware and aren't readily available. What's needed are standard, general-purpose drivers that can be adapted to more generic hardware. For instance, I would like to see an OS/2 driver for a CD-ROM drive, fax card, or tape drive, yet none are available. Why not? There are certainly more customers now who need OS/2 drivers. Without them, the operating system of choice may not be OS/2.
OS/2 2.0 won't make the task of writing device drivers any easier. True, version 2.0 will run DOS applications in protected mode, so the driver won't have to concern itself with bimodal operation. But the driver architecture for DOS programs will change radically. DOS programs will now call a Virtual Device Driver instead of accessing the device hardware directly. The VDD will massage the request and send it to a Physical Device Drive. The PDD will perform the low-level hardware communication with the device and send the data back to the VDD.
The VDD interface is new, while the PDD is nothing more than an OS/2 1.x bimodal driver with the real-mode sections removed. The VDD will emulate the BIOS and other interrupt functions, letting a DOS application assume it is talking directly with the device when it is actually communicating with the VDD. Protected-mode applications will continue to call OS/2 drivers, as in version 1.x, but can use 0:32 ("flat model") addressing.
In June, Microsoft announced a new device-driver architecture for mass storage devices called the layered device driver architecture (LADDR) Microsoft claims that LADDR can reduce by 90 percent the time to develop an OS/2 mass storage device driver. I hope this is true, but based on what I've seen so far, I wouldn't bet the farm on it.
A new DDK will come with standard driver code, so the developer need only add the code specific to the device itself to implement a fully functional driver. I haven't seen the new DDK yet, so I can't verify Microsoft's claims. At the time of this writing, Microsoft still had no firm release date for the LADDR kit. Non-mass storage drivers will continue to be written using conventional methods.
Neither IBM nor Microsoft has done enough to help the people trying to produce the drivers that OS/2 so desperately needs. The DDK upgrade from version 1.1 to 1.2 is way behind schedule, and the NDDK, used to develop network card drivers for the Extended Edition, is also late. The version 1.1 DDK does not work with PS/2 machines, so drivers must be developed on Industry Standard Architecture bus systems.
Information is still sketchy and incomplete. Although more books have appeared, none show examples of device drivers written in C. Most of the available documentation describes the DevHlp routines and their calling sequences, but not how to organize them into an actual driver.
What is needed is a driver writer's guide to take the mystery out of OS/2 driver writing. The guide should contain examples of actual drivers written in C, not scattered code fragments in assembly. It should also contain a list of helpful functions to aid in driver coding and debugging. Until such information becomes available, device drivers will remain the Achilles' heel of OS/2.