Jump to content

OS/2 Device Driver Frequently Asked Questions: Difference between revisions

From EDM2
Line 219: Line 219:


===How should a driver output strings to the display at boot time===
===How should a driver output strings to the display at boot time===
'''Question:'''
'''Question:'''What is the best way to output a string to the screen from the DD INIT routine and INIT_COMPLETE routinE?


'''Answer:'''  
In INIT I call DOSPUTMESSAGE. Is this function still available for INIT_COMPLETE - or does that depend on INIT segment of my code?
 
'''Answer:''' DosPutMessage is fine for INIT time. You could also do a DosWrite to the stdout device (File handle 1). Similiarly you can read keystrokes using a DosRead on the stdin device (File handle 0).
 
There is no way for a driver at INIT_COMPLETE time to output a message.
 
The only solution I can suggest is to fire up an application in startup.cmd or run from config.sys. And get that application to talk to the driver & report whether the INIT_COMPLETE was successful or not.
 
The only reason for the INIT_COMPLETE is to set up links between drivers (IDC) and you do it after all the INITs so as to remove any dependencies on the order of DEVICE='s in the config.sys. There should be no need to output strings at INIT_COMPLETE time.


===How should a base driver output strings to the display at boot time===
===How should a base driver output strings to the display at boot time===
'''Question:'''
'''Question:''' What is the best way to output a string to the screen from a base device driver at INIT time.
 
'''Answer:''' This is a little tricky.
 
DosPutMessage is fine for INIT time. However it is not available to Base Device drivers. Instead there is a special device helper that saves all the base device driver messages. At the end of base device driver initialisation all the saved messages are output to the display.
 
#include  "dhcalls.h"
USHORT APIENTRY DevHelp_Save_Message( NPBYTE MsgTable );
typedef struct _MSGTABLE {
  USHORT  MsgId;                      /* Message Id #                  */
  USHORT  cMsgStrings;                /* # of (%) substitution strings */
  PSZ      MsgString[1];              /* Substitution string pointers  */
} MSGTABLE;
typedef MSGTABLE *NPMSGTABLE;


'''Answer:'''
As you can see from the example, the save message helper requires you to use message ids.


===How can you tell the type of the PC's bus at INIT time===
===How can you tell the type of the PC's bus at INIT time===
'''Question:'''
'''Question:''' I want my device driver (PDD) to determine if it runs in an EISA PC environment at INIT time. Is there any support by the operating system to do this ?


'''Answer:'''  
'''Answer:''' There are some IOCTL calls a driver can make to talk to the TESTCFG driver and ask what type of bus the system is using.
 
The file bus.zip in the download area contains the source code you need to query the bus type, ISA, MCA or EISA.


===Function call hangs on return===
===Function call hangs on return===
'''Question:'''
'''Question:''' At INIT Time I use DosPutMessage() to print some messages on the Screen. If I call DosPutMessage() directly from my INIT-function, it works fine. But if I put DosPutMessage() in a separate function and call this function from my INIT function, the driver hangs upon return.


'''Answer:'''  
Why? Is this a limitation or a bug?
 
'''Answer:''' A bug.
 
This is a fairly common problem. If code (any code) is crashing on a return instruction the most likely cause is because the stack frame (which has the return address) is in some way damaged.
 
I would guess that you have declared the function incorrectly either
 
    declared as pascal & called as C
or
    declared as C called as pascal
 
If there is only one input parameter then both variants can be called ok, & will work, but both will crash when you return. Of course there could be another explanation, you have an errant pointer which is corrupting the stack :
 
function ()
{
    char arr[10], *p;
        p = &arr[0];
        *--p = 0;      // the return will "probably" crash
}


===Differences between OS/2 2.0 & 2.1 at initialisation time===
===Differences between OS/2 2.0 & 2.1 at initialisation time===
'''Question:'''
'''Question:''' I recently moved from OS/2 2.0 to version 2.1 and I noticed that my driver was being entered with some new strategy commands. Specifically, at boot time immediately after the INIT call, I get called again with function 1F. What is this function ?
 
I also noticed that my CS selector changed between the INIT function and the 1F function (from 0AEB to 0AE8). What is going on ?
 
'''Answer:''' The 1f function is new, its purpose is to tell all device drivers that all device drivers are now installed. It is referred to as INIT_COMPLETE.
 
The INIT_COMPLETE command is passed into drivers at initialisation time after all device drivers have been initialised. The context is strategy time and the drivers privilege level is 0.
 
The operation you should perform at INIT_COMPLETE is to make all your IDC attachments (see attachdd helper service).
 
OLD WAY
 
1) Drivers install at ring 3 with iopl set to 3
 
2) Applications open and start using drivers
 
NEW WAY
 
1) Drivers install at ring 3 with iopl set to 3
 
2) Drivers can continue to initialise and communicate with other drivers, at
ring 0 with iopl set to 2
 
3) Applications open and start using drivers
 
As regards your second question :
 
I also noticed that my CS selector changed between the INIT function and the 1F function (from 0AEB to 0AE8). What is going on ?
 
That is quite normal, the selectors are constructed so that the low order two bits contains the "Requestors Privilege Level" or RPL
 
At Init  time the RPL is 3 - 0AE8 orred 3 => 0AEB.
At 1F  time the RPL is 0 - 0AE8 or    0 => 0AE8.


'''Answer:'''


===Error code SYS1201 at Initialisation time===
===Error code SYS1201 at Initialisation time===
'''Question:'''
'''Question:''' I recently assembled a pysical DD with MS MASM 6.0. When I install it on my CONFIG.SYS file by typing: DEVICE=C:\........\XXXXX.SYS
 
I have 2 situation on 2 different configuration. On a PS/2 P70 all run OK. On a PS/2 95-V01 I have an error code SYS1201. Why?
 
'''Answer:''' When a driver is initialised it has the option of installing or not installing, the 1201 error means it is not installing, this could be for anyone of a million reasons - ALL UNDER THE CONTROL OF THE DRIVER -
 
So basically your driver has given up, without really telling you why.
 
It could be it could not acquire an interrupt because it was not available
"    "  "  "  "    "    "    an memory segment
"    "  "  "  "    "    "    a GDT selector
"    "  "  "  "    "    "    a timer
  anything in fact.
 
Two suggestions if the driver is not going to install call a standard routine with a message to display. The following is what I use.
 
#include
UINT abend (PREQPACKET rp, char *message);
static char    ActMessage[] = "Hit enter key to continue.\r\n";
static char    FailMessage[]  = " driver failed to install.\r\n";
static char    CrLf[]= "\r\n";
UINT abend (PREQPACKET rp, char *message)
{
    ULONG length;
    UCHAR ch;
    DosPutMessage(1, strlen(FailMessage), FailMessage);
    DosPutMessage(1, strlen (message), message);
    DosPutMessage(1, 2, CrLf);
    DosPutMessage(1, strlen(ActMessage), ActMessage);
    DosRead ((SHANDLE)0, (FARPOINTER)&ch, (USHORT)1, (FARPOINTER)&length);
    rp->s.InitExit.finalCS = 0;
    rp->s.InitExit.finalDS = 0;
    return RPDONE;
}
 
But you are desperate, so put an int 3 instruction in your drivers initialisation routine, reassemble and install it on the problem machine, using the kdb you should then be able to follow through the code to the point where the driver is setting :
 
    rp->s.InitExit.finalCS = 0;
    rp->s.InitExit.finalDS = 0;
        which will cause the 1201 result.


'''Answer:'''
If you apply (or already have) the change and you are still experiencing the same problem then there is an even simpler explanation. You are trying to install the same device driver twice.
 
This will work under DOS. Under OS/2 you cannot install multiple device drivers with the same name (and attribute). And anyway, why would you want to ?


===How can you tell which version of OS/2 you are using at INIT time===
===How can you tell which version of OS/2 you are using at INIT time===
'''Question:'''
'''Question:''' Is there a way to know during init time if a devide driver is running under OS/2 1.3 or OS/2 2.0. What sort of call,function or instruction can i use to find out ?
 
'''Answer:''' You can use the DosGetInfoSeg API at INIT time. This API provides the addresses of two data structures :
 
1) The Global info seg
2) The Local info seg
 
The addresses of these structures does not change so you only need to raise the query once. Once you have the address of these structures you can query their contents, at any time during the device drivers life. I've enclosed the code fragment I use to display the version number at Initialisation time.
 
    DosGetInfoSeg (&g, &l);
    Ginfo = MAKEP(g, 0);
    Linfo = MAKEP(l, 0);
//          Output OS/2 version number
    DosPutMessage(1, strlen(InitMessage1), InitMessage1);
    ch = Ginfo->uchMajorVersion;
    out_dec (ch);
    DosPutMessage(1, 1, ".");
    ch = Ginfo->uchMinorVersion;
    out_dec (ch);
    DosPutMessage(1, strlen(VersMessage2), VersMessage2);
    out_dec (ch);
    DosPutMessage(1, 1, &ch);
    DosPutMessage(1, 2, CrLf);


'''Answer:'''


===Calling API's at INIT time===
===Calling API's at INIT time===
'''Question:'''
'''Question:''' I have seen an example device driver that invokes a DosExecPgm during initialisation. Is this possible ?


'''Answer:'''  
'''Answer:''' Yes it is, so long as the file system is ready (so you can read the DLL's), you can call any and all the .DLLs on the computer, HOWEVER the the sub-systems required to support/cooperate with those dlls, may not be ready to go at init time . This means that the dll call may appear to work at init time but it may not work in the next release of the OS.
 
The ONLY APIs documented as supported at init time are - DosBeep & all the file handling APIs. Unless there is a very good reason, I would suggest complying with the documentation.


===SYS1719 error after loading a PDD===
===SYS1719 error after loading a PDD===
'''Question:'''
'''Question:''' What are the posible reasons for SYS1719 error after loading of PDD?
 
'''Answer:''' The output from "help sys1719" states :
 
SYS1719: The file "***" specified in the *** command on line *** of the CONFIG.SYS file does not contain a valid device driver or file system driver.  Line *** is ignored.
 
EXPLANATION: The file specified does not contain a valid device driver or file system driver, or contains a valid DOS device driver when a DOS session was not started.
 
ACTION: Perform one of the following actions, then restart the system:
1. Edit the CONFIG.SYS file to correct or remove the incorrect command.
2. Edit the CONFIG.SYS file to remove the PROTECTONLY=YES command.
3. Install the correct device driver or file system driver in the specified file.
4. Install all dynamic link libraries required by the specified device driver.
 
There are a number of reasons your driver may be considered invalid, if you have a map file for it, check :
 
    That the driver contains a data & a code segment IN THAT PHYSICAL ORDER
    The very first data item in the data segment is the drivers header.
    Quite often when people are building drivers, the device header (WHICH MUST BE AT OFFSET ZERO) is placed incorrectly in the data segment.
    If you have the utility exehdr then you can try examining the driver with that, ie EXEHDR MOUSE.SYS :
    Microsoft (R) EXE File Header Utility  Version 2.01
    Copyright (C) Microsoft Corp 1985-1990.  All rights reserved.
    Library:                  MOUSE
    Description:              mouse.DLL
    Data:                    SHARED
    Initialization:          Global
    Initial CS:IP:            seg  0 offset 0000
    Initial SS:SP:            seg  0 offset 0000
    DGROUP:                  seg  1
    PROTMODE
    no. type address  file  mem  flags
      1 DATA 00000110 001f1 01430 SHARED, PRELOAD
      2 CODE 00000320 01c9a 01c9a PRELOAD
      3 CODE 00001ff0 01ac3 01ac4 PRELOAD, IOPL, (movable)
 


'''Answer:'''


===Where in memory, is a device driver loaded===
===Where in memory, is a device driver loaded===
'''Question:'''
'''Question:''' If I got the previous conversation right in this area, then device driver will be loaded with its first DataSeg and CodeSeg below the 1 MB border. Is this correct ?
 
'''Answer:'''
Yes, it is correct for OS/2 1.x.
No, it is wrong for OS/2 2.x


'''Answer:'''
Under OS/2 1.x, it was a problem too, as all those OS/2 drivers sucked up the DOSboxes memory. Under 2.X however the physical ram for the dd's code & data is coming from the TOP of memory not the bottom.


===Making messages to display at Initialisation time===
===Making messages to display at Initialisation time===
'''Question:'''
'''Question:''' I have heard of a program called MKMSGF which can be used to generate some sort of generic message information which allows a driver to support foreign languages very simply. It seems to have some relationship with a DLL call LANMSGDD.


'''Answer:'''  
Do you know where I might find out detailed information about this, so I can look at include such support in my drivers.
 
'''Answer:''' The MKMSGF & the MSGBIND utilities allow you to bind text messages into an application AFTER it has been compiled & linked. All OS/2 's error and warning messages work this way.
 
The intention is that a developer can produce some code which is then given to the distributors. The distributors can then apply all the text messages to the app. The benefit is that the distributor can use different message files for different countries and thus (hopefully) "internationalise" the application, to suit the end user.
 
There is info (and examples) on how to use these utilities in the toolkit tools reference online help.
 
The rules for using messages bound in this way from within a device driver; you can either use the DosGet/Put/Ins message APIs or alternatively there is a helper service (0x3d). In the 2.0 doc Physical Device Driver Ref, this helper is called SAVE_MESSAGE 17-26. This is a typo its real name is DispMsg - which is why it appears between DevDone & DynamicAPI. Messages can only be displayed in this way by device drivers at initialisation time.


===How can I query the OS/2 language version at initialisation time===
===How can I query the OS/2 language version at initialisation time===
'''Question:'''
'''Question:''' I want my driver query the language version of the system so I can output messages in the correct language.


'''Answer:'''  
'''Answer:''' There are several ways to do this :
 
* Open the keyboard driver (DosOpen ("KBD$", ...)) and pass in an IOCtl request to query the current codepage.
* Use the DevHelp_GetDOSVar to read the Codepage field from the Global Info Seg.
* Parse the config.sys file to read and interpret the CODEPAGE= line.
 
But really all the above solutions are wrong. The correct approach would be to use Message files. You should create one message file for each language you intend to support, then when the driver is installed you should let the user decide which language variant should be installed.


===Can I use an IDC link at Initialisation time===
===Can I use an IDC link at Initialisation time===
'''Question:'''
'''Question:''' I have created an IDC link to another driver. I used the AttachDD helper service to create the link at initialisation time. When I tried to call the other driver using the IDC link the system hung. Why ?


'''Answer:'''
'''Answer:''' The IDC link information is for a Ring 0 call, into the Attached driver. At Init time your driver is running at Ring 3. It is not possible to call from Ring 3 to Ring 0 in this way. Give up it cannot be done.
 
Instead, you should delay ATTACHing to other drivers until all the drivers have been installed. You do this by adding support for the INIT_COMPLETE command. Once your driver receives the INIT_COMPLETE, you know that all drivers will be installed. You should now establish the IDC link using the AttachDD helper service. Once this is done you can start using the IDC interface, immediately.


==Start of operations==
==Start of operations==

Revision as of 17:35, 17 June 2012

By Tim Snape

This article contains a selection of frequently asked questions and is intended to be used by anyone who is writing or planning to write OS/2 device drivers.


Support Categories

Situations when it is (un)necessary to write an OS/2 device driver

Is a driver necessary to use IRQs

Question: I have a requirement to intercept hardware interrupts from an adapter. Do I need to write a device driver.

Answer: Yes, a device driver is absolutely the only way to interact with hardware generated interrupts.

Is a driver necessary to access I/O ports

Question: I have a requirement to read and write to I/O ports. Is it necessary for me to write an OS/2 driver.

Answer: No, it is not.

Under OS/2 there are four levels of privilege (referred to as rings)

  • Ring 3 - Least privilege, this is where applications normally reside
  • Ring 2 - Next highest privilege. Code which is running at Ring 2 may access I/O ports under OS/2. For this reason this privilege is often referred to as I/O privilege level or IOPL.
  • Ring 1 - Is not used under OS/2
  • Ring 0 - Is the highest privilege level and is used by device drivers & the operating system.

In order to access an I/O port (and also to disable/enable the interrupt flag) a program MUST be running at ring 2. The way this is done is to specify in the programs definition file that a particular segment will have IOPL (IO privilege).

Next comes the clever bit. The compiler/ling will generate an executable that contains a special (IOPL) code segment. When the program is loaded, the loader will see that there is an IOPL segment & it will create a special mechanism that will allow that segment to run at ring 2.

For the technically knowledgable the loader creates a ring 2 call gate.

When the IOPL segment (containing the I/O access code) is called, privilege is changed to ring 2, and I/O access can be performed.

N.B. There is an overhead in this transition so if you plan to implement a polling loop on an I/O port (or something equally horrible) then be warned.

For some example code that accesses I/O ports go to the file download area.

Is a driver necessary to access memory on an adapter card

Question: I have a requirement to read and write to specific areas in memory in the range D000:0 to D800:0. This is to interface with a special function option card.

There is no time criticality or interrupts to deal with, so writing an OS/2 device driver does seem rather over-the-top.

In DOS, no problem. With MS-Windows, there are Global Selectors pre-defined ( _D000 etc. ).

How can this be done using OS/2.

Answer: At some point you will HAVE to use a driver to access the RAM directly. However it is not necessary to write a driver from scratch. Instead use one of OS/2's drivers to do the job for you.

Failing that, there are a number of drivers written by third parties that will provide you with this function.

I have placed some code in the anonymous ftp area that can be used to access memory directly. Download the Technical Developers Toolkit. The interesting file in the .zip is parallel.c, this contains code that reads the Bios data area used to hold the parallel port addresses.

Accessing Video Ram

Question: I need to access the memory mapped video ram do I need a driver

Answer: There is already support for obtaining addressability in the way you require. I suspect you don't need a device driver. You can access the frame buffer directly, use memory-mapped and port IO from the app level.

A note on the physical memory address space. Your graphics adapter memory can live anywhere above the end of physical memory (RAM) up to the 4Gb limit (0FFFFFFFFh). To see the memory, try as Tim suggested, using the KDB to dump physical memory, %%address.

There is an IOCTl supported by the SCREEN$ device driver for this purpose. You'll need to know the physical address and the aperture size, ie. how much memory the adapter has. See the include file bsedev.h and the definitions for SCREENDD_GETLINEARACCESS. The IOCTl will create and return a linear address which maps to the physical for you to use.

The flags field in the call is critical and one reason I asked what you're writing. They allow you to choose where in OS/2s linear address space the mapping occurs 1) in the current process address space 2) in the shared arena 3) in global space. BTW the flags map directly to those for VMAlloc except the ATTACH bit which causes a DevHlp_ProcessToGlobal.


Application Issues

Accessing IO ports at application level

Question: I have a requirement to access I/O ports from a 32 bit application. I know all about IOPL privilege BUT the only way to create a ring 2 IOPL segment seems to be to use a 16 bit DLL, is this right ?

Answer: Unfortunately yes, there is a more complete explanation of IOPL and accessing I/O ports in the Is a driver necessary section, so I will not repeat it here.

Basically though ring 2 IOPL segments can only be 16 bit, there are however several approaches to solving your problem.

  • Use another driver, one that already exists. The TESTCFG driver contains code that allows you to access I/O port addresses higher than hex 100.
  • Package up your 16 bit IOPL segment into a DLL and call that from your 32 bit code segment.
  • Write a driver from scratch and get the driver to perform the I/O on behalf of the application.
  • Write a driver from scratch and get the driver to set the IOPL bits in the EFLAGS register to 3. This will allow the calling thread to perform I/O at ring 3.

The last (rather neat) suggestion was supplied by Holger Veit. It should be noted that patching the EFLAGS register will only allow the calling thread to access I/O ports.

Read buffers greater than 64K

Question: When performing Read/Write operations can I use data buffers larger than 64K.

Answer: No you cannot. The Read/Write device driver commands have a length field which is a 16 bit word, this implies that the largest I/O operation is 64K.

If you really want to perform I/O on regions greater than 64K, then you should use an IOCTL command to pass in the address of a 32 bit memory item.

Compiling and building your OS/2 device driver

Using the Microsoft C compiler

Question: Should I use the Microsoft C6 compiler & if yes, where can I buy it.

Answer: Back in the old days when IBM & Microsoft were talking to each other, all device drivers were written using Microsoft C compilers. Usually Version 6.00a.

Unhappily following the rift between the two companies Microsoft withdrew support for the C6 product & brought out new versions. These versions were incompatible with OS/2.

Many people today still use the C6 compiler, many examples exist based on the C6 compiler & many companies still recommend using the C6 compiler - but you cannot buy it.

So if you want an easy life use the C6 compiler, how you acquire the tool is the real question. But that cannot be answered here.

If you elect to purchase an alternative C compiler to generate your driver look at the other QA's in this category.


Using the Borland C compiler

Question: How do I use the Borland compiler

Answer: You should refer to Borlands web site for support questions.

Using the Watcom C compiler

Question: How do I use the Watcom compiler version 10.5

Answer: Watcom is a recommended supplier of 'C' compilers for OS/2 device driver developers. IBM development are now widely using the product. The Watcom 'C' compiler comes with sample OS/2 device drivers plus a number of useful utilities. For detailed support information on using the Watcom product you should refer to Watcoms web site.

When using Watcom for the first time you may experience compatibility problems linking to libraries compiled using other products. The following comments assume you are using the dhcalls.lib helper library found in the Devcon DDK

  1. Instead of _acrtused, the Watcom C runtime library looks for cstart_. This should be declared as public in the .asm file.
  2. Watcom provides a special pragma, cdecl, for specifying the calling convention used by MSC. They also provide a _Cdecl keyword, as in 'int _Cdecl main(...);'.
  3. The Watcom naming convention is to append an underscore to function & variable names. Under Microsoft 'C' the convention is to prefix the names with underscore :
 Watcom function name  = name_
 MSC     function name = _name

Therefore all references to Watcom 'C' names from assembler code should assume a trailing underscore. All references to Microsoft 'C' names from assembler code should assume leading underscores. The reverse should be assumed when referencing assembler names from Watcom or MS 'C' code.

  1. The compiler flag -Zu must be used.
  2. The "_Seg16" keyword is not legal when compiling with WCC(Watcom 16bit), therefore in all declarations "_Seg16" must be replaced with "_far". Because of this some of the toolkit headers like OS2DEF.H must be changed.
  3. The "PASCAL" keyword must be changed to "pascal".
  4. There is a library of device driver helper functions, that is callable from Watcom 'C'. This library can be found on the DDK BBS.
  5. The symbol files generated by Watcoms linker are incompatible with the Kernel debugger. If you use Watcom .sym files and the kernel debugger then the system will crash at boot time, with the error message :
 Internal symbol error: SegDefLinkUp

There is a utility for translating Watcom .sym files to the correct format. This utility is called WAT2MAP.EXE and can be found on the DUDE BBS.

  1. The Watcom 'C' compiler is more rigorous in its compliance with the ANSI 'C' standard. This may cause minor problems when porting from other 'C' compilers.

There was an article on porting OS/2 device drivers to Watcom 'C' in the Devcon 7 newsletter.

Please note that according to Watcom technical support, there are some problems with using NMAKE with the Watcom compilers/linkers.

Using the CSet compiler

Question: How do I use the CSet compiler

Answer: You should not. CSet generates 32-bit code this is incompatible with the the 16 bit architecture of OS/2's device drivers.

Will IBM fix CSet so that it can generate OS/2 device drivers

Question: Will IBM fix CSet so that it can generate OS/2 device drivers

Answer: No, the stated policy on CSet is that it will NOT and NEVER will generate 16 bit code. The reason IBM's CSet people give for this drastic decision, is that there already exist many, many 16 bit 'C' compiler products.

In fact this will all change when OS/2 for Power hits the streets. OS/2 for Power uses a 32 bit driver model so it will be possible to use CSet for drivers in the future.

Using the CSet compiler to generate 32 bit driver code

Question: How do I use the CSet compiler to generate 32 bit code for linking into a 16 bit device driver.

Answer: This can be done. The security kernel consists of 16 bit assembler code which is linked with 32 bit code emitted by CSet.

There is an example device driver that does this however it is not for the faint hearted. In order to get the code to work you must first supply a (complete) replacement run-time library. The body of the driver is pure 16 bit assembler and the CSet code is bolted onto this.

The way this code is implemented is very specific to the security kernel, however the message is it can be done

Using Steve Mastriannis example 'C' code.

Question: I am about to write a device driver for a simple network now. Before starting this I am reading the book "Writing OS/2 2.1 device drivers in C" by Steven J. Mastriani. In this book they write about a C callable 2.0 Devhlp library and a toolkit. About this I have a few questions. I don't have this C callable Devhlp library so I will have to call this function from an assembler routine. My problem is that I can not find any "prototype" of this function anywhere. I suppose these are different for all Devhlp functions. Where do I find this?

Answer: Steve supplies these helper libraries as seperate products. I think the retail price is 129 USD.

There are a number of alternative helper libraries. These can be found in the Devcon DDK product.

It is unfortunate that IBM was not able to standardise on a single library of helper functions. The result is everyone tends to create their own.

Problem using Microsoft library

Question: In S.J.Mastrianni's book (second edition page 498 3rd line from the end and in manyother places) he uses the library "llibcep.lib" from the Microsoft Compiler LIB directory. I did a full instalation of the compiler (Version 6.0A) and I can not find the librery.

Answer: The files installed & their names will vary depending on the installation options you typed in when you ran the MS setup program.

The name

llibcep
  l = represents large memory model
  s = represents small memory model
  e = represents floating point emulation
  7 = represents floating point in 8087 hardware
  p = represents protected mode
  r = represents real mode

One of the installation options asks "use default naming convention" in which case your llibcep.lib file is renamed llibce.lib

To fix it either rename or copy the file to llibcep or modify your link parameters so it uses the "correct" default name of llibce.lib.

Where are the 16 bit DOS library functions

Question: I wrote a driver under OS/2 1.3, but now under Warp the linker cannot find the DOS library functions, DosOpen, DosRead etc., where are these functions ?

Answer: These functions used to be in the file DOSCALLS.LIB, this file is no longer shipped with the Warp Toolkit, instead you should use the library OS2286.LIB.

Alternatively you could copy your version of DOSCALLS.LIB from the earlier release to your current development system.

Initialising the driver for the first time

When you reboot, the device driver will not install

Question: The driver has been compiled & linked with no apparent problems but at initialisation time the kernel reports that the driver is in an incorrect format.

Answer:There are a number of rules for the layout of OS/2 device drivers :

  • The first segment MUST be a data segment.
  • The data segment MUST contain a header structure at the very beginning.
  • The header structure must be in the correct format.

Data items quite often get placed in front of the device driver header structure, there is a tool called DHDR.EXE that will dump the contents of a device driver files header.

At initialisation time the device driver is responsible for telling the kernel the length of the drivers code & data segments. The kernel then uses the information to limit the amount of storage allocated for the drivers code & data.

If you forget to set these fields up then by the values used will be invalid and your driver will not have very long to live.

Similiarly if the value supplied by the driver is too short, then the moment you pass control into this non-existent code space the driver will crash.

There are a number of ways of identifying this problem and removing it.

  • You can examine the .MAP file, if a code function occurs after the initialisation code then you will probably want to move code around.
  • Place special markers at the end of the data & code segments. In the data segments the marker could be a text string e.g. char *eodata = "END OF DATA"; In the code segment the marker could be a function (not a static) with a special name.
  • The simplest approach is to say (at init time) that the size of the code & data space is the size of the currently loaded segments (LSL assembler instruction). This way you are guaranteed that all the code will be in the correct place.

In the bad old days of DOS & OS/2 1.x memory for device drivers was at a premium, with OS/2 2.x & Warp this is no longer the case. Make life easy for yourself and do not worry about saving that last 100 bytes of code.

How should a driver output strings to the display at boot time

Question:What is the best way to output a string to the screen from the DD INIT routine and INIT_COMPLETE routinE?

In INIT I call DOSPUTMESSAGE. Is this function still available for INIT_COMPLETE - or does that depend on INIT segment of my code?

Answer: DosPutMessage is fine for INIT time. You could also do a DosWrite to the stdout device (File handle 1). Similiarly you can read keystrokes using a DosRead on the stdin device (File handle 0).

There is no way for a driver at INIT_COMPLETE time to output a message.

The only solution I can suggest is to fire up an application in startup.cmd or run from config.sys. And get that application to talk to the driver & report whether the INIT_COMPLETE was successful or not.

The only reason for the INIT_COMPLETE is to set up links between drivers (IDC) and you do it after all the INITs so as to remove any dependencies on the order of DEVICE='s in the config.sys. There should be no need to output strings at INIT_COMPLETE time.

How should a base driver output strings to the display at boot time

Question: What is the best way to output a string to the screen from a base device driver at INIT time.

Answer: This is a little tricky.

DosPutMessage is fine for INIT time. However it is not available to Base Device drivers. Instead there is a special device helper that saves all the base device driver messages. At the end of base device driver initialisation all the saved messages are output to the display.

#include  "dhcalls.h"

USHORT APIENTRY DevHelp_Save_Message( NPBYTE MsgTable );

typedef struct _MSGTABLE {

  USHORT   MsgId;                       /* Message Id #                  */
  USHORT   cMsgStrings;                 /* # of (%) substitution strings */
  PSZ      MsgString[1];               /* Substitution string pointers  */
} MSGTABLE;

typedef MSGTABLE *NPMSGTABLE;

As you can see from the example, the save message helper requires you to use message ids.

How can you tell the type of the PC's bus at INIT time

Question: I want my device driver (PDD) to determine if it runs in an EISA PC environment at INIT time. Is there any support by the operating system to do this ?

Answer: There are some IOCTL calls a driver can make to talk to the TESTCFG driver and ask what type of bus the system is using.

The file bus.zip in the download area contains the source code you need to query the bus type, ISA, MCA or EISA.

Function call hangs on return

Question: At INIT Time I use DosPutMessage() to print some messages on the Screen. If I call DosPutMessage() directly from my INIT-function, it works fine. But if I put DosPutMessage() in a separate function and call this function from my INIT function, the driver hangs upon return.

Why? Is this a limitation or a bug?

Answer: A bug.

This is a fairly common problem. If code (any code) is crashing on a return instruction the most likely cause is because the stack frame (which has the return address) is in some way damaged.

I would guess that you have declared the function incorrectly either

   declared as pascal & called as C

or

   declared as C called as pascal

If there is only one input parameter then both variants can be called ok, & will work, but both will crash when you return. Of course there could be another explanation, you have an errant pointer which is corrupting the stack :

function ()
{
   char arr[10], *p;

       p = &arr[0];
       *--p = 0;       // the return will "probably" crash
}

Differences between OS/2 2.0 & 2.1 at initialisation time

Question: I recently moved from OS/2 2.0 to version 2.1 and I noticed that my driver was being entered with some new strategy commands. Specifically, at boot time immediately after the INIT call, I get called again with function 1F. What is this function ?

I also noticed that my CS selector changed between the INIT function and the 1F function (from 0AEB to 0AE8). What is going on ?

Answer: The 1f function is new, its purpose is to tell all device drivers that all device drivers are now installed. It is referred to as INIT_COMPLETE.

The INIT_COMPLETE command is passed into drivers at initialisation time after all device drivers have been initialised. The context is strategy time and the drivers privilege level is 0.

The operation you should perform at INIT_COMPLETE is to make all your IDC attachments (see attachdd helper service).

OLD WAY

1) Drivers install at ring 3 with iopl set to 3

2) Applications open and start using drivers

NEW WAY

1) Drivers install at ring 3 with iopl set to 3

2) Drivers can continue to initialise and communicate with other drivers, at ring 0 with iopl set to 2

3) Applications open and start using drivers

As regards your second question :

I also noticed that my CS selector changed between the INIT function and the 1F function (from 0AEB to 0AE8). What is going on ?

That is quite normal, the selectors are constructed so that the low order two bits contains the "Requestors Privilege Level" or RPL

At Init  time the RPL is 3 - 0AE8 orred 3 => 0AEB.
At 1F   time the RPL is 0 - 0AE8 or    0 => 0AE8.


Error code SYS1201 at Initialisation time

Question: I recently assembled a pysical DD with MS MASM 6.0. When I install it on my CONFIG.SYS file by typing: DEVICE=C:\........\XXXXX.SYS

I have 2 situation on 2 different configuration. On a PS/2 P70 all run OK. On a PS/2 95-V01 I have an error code SYS1201. Why?

Answer: When a driver is initialised it has the option of installing or not installing, the 1201 error means it is not installing, this could be for anyone of a million reasons - ALL UNDER THE CONTROL OF THE DRIVER -

So basically your driver has given up, without really telling you why.

It could be it could not acquire an interrupt because it was not available " " " " " " " an memory segment " " " " " " " a GDT selector " " " " " " " a timer

 anything in fact.

Two suggestions if the driver is not going to install call a standard routine with a message to display. The following is what I use.

#include 

UINT abend (PREQPACKET rp, char *message);

static char     ActMessage[] = "Hit enter key to continue.\r\n";
static char     FailMessage[]  = " driver failed to install.\r\n";
static char     CrLf[]= "\r\n";

UINT abend (PREQPACKET rp, char *message)
{
   ULONG length;
   UCHAR ch;

   DosPutMessage(1, strlen(FailMessage), FailMessage);
   DosPutMessage(1, strlen (message), message);
   DosPutMessage(1, 2, CrLf);
   DosPutMessage(1, strlen(ActMessage), ActMessage);

   DosRead ((SHANDLE)0, (FARPOINTER)&ch, (USHORT)1, (FARPOINTER)&length);

   rp->s.InitExit.finalCS = 0;
   rp->s.InitExit.finalDS = 0;

   return RPDONE;
}

But you are desperate, so put an int 3 instruction in your drivers initialisation routine, reassemble and install it on the problem machine, using the kdb you should then be able to follow through the code to the point where the driver is setting :

   rp->s.InitExit.finalCS = 0;
   rp->s.InitExit.finalDS = 0;
       which will cause the 1201 result.

If you apply (or already have) the change and you are still experiencing the same problem then there is an even simpler explanation. You are trying to install the same device driver twice.

This will work under DOS. Under OS/2 you cannot install multiple device drivers with the same name (and attribute). And anyway, why would you want to ?

How can you tell which version of OS/2 you are using at INIT time

Question: Is there a way to know during init time if a devide driver is running under OS/2 1.3 or OS/2 2.0. What sort of call,function or instruction can i use to find out ?

Answer: You can use the DosGetInfoSeg API at INIT time. This API provides the addresses of two data structures :

1) The Global info seg 2) The Local info seg

The addresses of these structures does not change so you only need to raise the query once. Once you have the address of these structures you can query their contents, at any time during the device drivers life. I've enclosed the code fragment I use to display the version number at Initialisation time.

   DosGetInfoSeg (&g, &l);
   Ginfo = MAKEP(g, 0);
   Linfo = MAKEP(l, 0);

//          Output OS/2 version number
   DosPutMessage(1, strlen(InitMessage1), InitMessage1);

   ch = Ginfo->uchMajorVersion;
   out_dec (ch);
   DosPutMessage(1, 1, ".");
   ch = Ginfo->uchMinorVersion;
   out_dec (ch);

   DosPutMessage(1, strlen(VersMessage2), VersMessage2);
   out_dec (ch);
   DosPutMessage(1, 1, &ch);

   DosPutMessage(1, 2, CrLf);


Calling API's at INIT time

Question: I have seen an example device driver that invokes a DosExecPgm during initialisation. Is this possible ?

Answer: Yes it is, so long as the file system is ready (so you can read the DLL's), you can call any and all the .DLLs on the computer, HOWEVER the the sub-systems required to support/cooperate with those dlls, may not be ready to go at init time . This means that the dll call may appear to work at init time but it may not work in the next release of the OS.

The ONLY APIs documented as supported at init time are - DosBeep & all the file handling APIs. Unless there is a very good reason, I would suggest complying with the documentation.

SYS1719 error after loading a PDD

Question: What are the posible reasons for SYS1719 error after loading of PDD?

Answer: The output from "help sys1719" states :

SYS1719: The file "***" specified in the *** command on line *** of the CONFIG.SYS file does not contain a valid device driver or file system driver. Line *** is ignored.

EXPLANATION: The file specified does not contain a valid device driver or file system driver, or contains a valid DOS device driver when a DOS session was not started.

ACTION: Perform one of the following actions, then restart the system: 1. Edit the CONFIG.SYS file to correct or remove the incorrect command. 2. Edit the CONFIG.SYS file to remove the PROTECTONLY=YES command. 3. Install the correct device driver or file system driver in the specified file. 4. Install all dynamic link libraries required by the specified device driver.

There are a number of reasons your driver may be considered invalid, if you have a map file for it, check :

   That the driver contains a data & a code segment IN THAT PHYSICAL ORDER
   The very first data item in the data segment is the drivers header.
   Quite often when people are building drivers, the device header (WHICH MUST BE AT OFFSET ZERO) is placed incorrectly in the data segment.
   If you have the utility exehdr then you can try examining the driver with that, ie EXEHDR MOUSE.SYS :

   Microsoft (R) EXE File Header Utility  Version 2.01
   Copyright (C) Microsoft Corp 1985-1990.  All rights reserved.

   Library:                  MOUSE
   Description:              mouse.DLL
   Data:                     SHARED
   Initialization:           Global
   Initial CS:IP:            seg   0 offset 0000
   Initial SS:SP:            seg   0 offset 0000
   DGROUP:                   seg   1
   PROTMODE

   no. type address  file  mem   flags
     1 DATA 00000110 001f1 01430 SHARED, PRELOAD
     2 CODE 00000320 01c9a 01c9a PRELOAD
     3 CODE 00001ff0 01ac3 01ac4 PRELOAD, IOPL, (movable)


Where in memory, is a device driver loaded

Question: If I got the previous conversation right in this area, then device driver will be loaded with its first DataSeg and CodeSeg below the 1 MB border. Is this correct ?

Answer: Yes, it is correct for OS/2 1.x. No, it is wrong for OS/2 2.x

Under OS/2 1.x, it was a problem too, as all those OS/2 drivers sucked up the DOSboxes memory. Under 2.X however the physical ram for the dd's code & data is coming from the TOP of memory not the bottom.

Making messages to display at Initialisation time

Question: I have heard of a program called MKMSGF which can be used to generate some sort of generic message information which allows a driver to support foreign languages very simply. It seems to have some relationship with a DLL call LANMSGDD.

Do you know where I might find out detailed information about this, so I can look at include such support in my drivers.

Answer: The MKMSGF & the MSGBIND utilities allow you to bind text messages into an application AFTER it has been compiled & linked. All OS/2 's error and warning messages work this way.

The intention is that a developer can produce some code which is then given to the distributors. The distributors can then apply all the text messages to the app. The benefit is that the distributor can use different message files for different countries and thus (hopefully) "internationalise" the application, to suit the end user.

There is info (and examples) on how to use these utilities in the toolkit tools reference online help.

The rules for using messages bound in this way from within a device driver; you can either use the DosGet/Put/Ins message APIs or alternatively there is a helper service (0x3d). In the 2.0 doc Physical Device Driver Ref, this helper is called SAVE_MESSAGE 17-26. This is a typo its real name is DispMsg - which is why it appears between DevDone & DynamicAPI. Messages can only be displayed in this way by device drivers at initialisation time.

How can I query the OS/2 language version at initialisation time

Question: I want my driver query the language version of the system so I can output messages in the correct language.

Answer: There are several ways to do this :

  • Open the keyboard driver (DosOpen ("KBD$", ...)) and pass in an IOCtl request to query the current codepage.
  • Use the DevHelp_GetDOSVar to read the Codepage field from the Global Info Seg.
  • Parse the config.sys file to read and interpret the CODEPAGE= line.

But really all the above solutions are wrong. The correct approach would be to use Message files. You should create one message file for each language you intend to support, then when the driver is installed you should let the user decide which language variant should be installed.

Can I use an IDC link at Initialisation time

Question: I have created an IDC link to another driver. I used the AttachDD helper service to create the link at initialisation time. When I tried to call the other driver using the IDC link the system hung. Why ?

Answer: The IDC link information is for a Ring 0 call, into the Attached driver. At Init time your driver is running at Ring 3. It is not possible to call from Ring 3 to Ring 0 in this way. Give up it cannot be done.

Instead, you should delay ATTACHing to other drivers until all the drivers have been installed. You do this by adding support for the INIT_COMPLETE command. Once your driver receives the INIT_COMPLETE, you know that all drivers will be installed. You should now establish the IDC link using the AttachDD helper service. Once this is done you can start using the IDC interface, immediately.

Start of operations

Using Request Packets

A collection of commonly asked questions about the IOCTL interface

Defining and Using Semaphores

How the stack works

Using DMA

Defining and Using memory

32 Bit issues compiling/linking/running

How time works

Using interrupts

Shutting down OS/2 gracefully

What differences are there between the versions of OS/2

Debugging tips, tricks and treats

A collection of tools & information on tools for device driver authors

General useful information and support

Other sources of information and support

Download