OS/2 Device Driver Frequently Asked Questions: Difference between revisions
Line 196: | Line 196: | ||
'''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. | '''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:''' | '''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=== | ===How should a driver output strings to the display at boot time=== |
Revision as of 17:27, 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
- Instead of _acrtused, the Watcom C runtime library looks for cstart_. This should be declared as public in the .asm file.
- 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(...);'.
- 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.
- The compiler flag -Zu must be used.
- 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.
- The "PASCAL" keyword must be changed to "pascal".
- There is a library of device driver helper functions, that is callable from Watcom 'C'. This library can be found on the DDK BBS.
- 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.
- 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:
Answer:
How should a base driver output strings to the display at boot time
Question:
Answer:
How can you tell the type of the PC's bus at INIT time
Question:
Answer:
Function call hangs on return
Question:
Answer:
Differences between OS/2 2.0 & 2.1 at initialisation time
Question:
Answer:
Error code SYS1201 at Initialisation time
Question:
Answer:
How can you tell which version of OS/2 you are using at INIT time
Question:
Answer:
Calling API's at INIT time
Question:
Answer:
SYS1719 error after loading a PDD
Question:
Answer:
Where in memory, is a device driver loaded
Question:
Answer:
Making messages to display at Initialisation time
Question:
Answer:
How can I query the OS/2 language version at initialisation time
Question:
Answer:
Can I use an IDC link at Initialisation time
Question:
Answer: