TITLE: Writing Device Drivers - Multiple Segments
IBM Developer Connection News Volume 7
by Steve Mastrianni
Occasionally I need to write a driver that occupies multiple
code or multiple data segments. If you follow the guidelines
presented here, you can avoid hours of frustrating development
time.
The most fundamental change you will make is to use all far
pointers and functions. This allows you reference functions and
data across the 64KB segment boundaries, and requires the use of
far calls to the DevHlp libraries. You can do this by modifying
the DHCalls library on The Developer Connection Device Driver Kit
for OS/2 or, if you are using the DevHlp library from Personal
Systems Software, you can call (203) 693-0404 for an upgrade.
Once you've converted the DevHlp library calls to FAR procedures,
you must be sure to remove any NEAR references in your assembly
language and C code. You'll need to change your compiler switches
to use FAR calls as follows:
cl -c -Alfu -Gs /NT_TEXT -G2 -Zl -Zp yourfile.c
The /NT_TEXT parameter names the segment and allows you to order
the segments at link time. As your first code segment approaches
the 64KB boundary, you can name new code segments to a different
name (for example, /NT_TEXT2), which will let you place the code
in a second segment using the assembly language startup code and
.DEF file. Of course, you must link with OS2286.LIB and the
large model library, LLIBCEP.LIB, instead of SLIPCEP.LIB.
.SEQ
_DATA segment word public 'DATA'
_DATA ends
CONST segment word public 'CONST'
CONST ends
_BSS segment word public 'BSS'
_BSS ends
FAR_BSS segment word public 'FAR_BSS'
FAR_BSS ends
DGROUP group FAR_BSS, CONST, _BSS, DATA, _DATA
_TEXT segment word public 'CODE'
assume cs:_TEXT, ds:DGROUP, es:NOTHING, ss:NOTHING
.286P
;
_STRATEGY proc far
__acrtused: ; to satisfy EXTRN in C-generated modules
;
.
.
ret
;
_STRATEGY endp
_INT_HANDLER proc far
_INT_HANDLER endp
_TIM_HANDLER proc far
_TIM_HANDLER endp
_TEXT ends
_TEXT2 segment word public 'CODE'
_TEXT2 ends
RMCode segment word public 'CODE'
RMCode ends
; stick RM code in second segment
CGROUP group _TEXT2,RMCode
end
Sample Code 1. Startup code
Next, you must initialize all of your global variables. If you
don't, the linker will attempt to place them in a second data
segment ahead of your first code segment. Initializing your
variables forces them into the default data segment. Once
you've made the changes, compile and link your device driver and
examine the map file to be sure all of your variables are
initialized and reside in the default data segment.
LIBRARY YOURLIB
PROTMODE
SEGMENTS
_DATA CLASS'DATA' PRELOAD
CONST CLASS'CONST' PRELOAD
_BSS CLASS'BSS' PRELOAD
FAR_BSS CLASS'FAR_BSS' PRELOAD
_TEXT CLASS'CODE' PRELOAD
_TEXT2 CLASS'CODE' PRELOAD IOPL
RMCode CLASS'CODE' PRELOAD IOPL
Sample Code 2. .DEF file for extra code segment
You will have to change your .DEF file to order the segments
correctly, and to mark the extra segments as IOPL. This will
keep them around long enough to lock them down, which is your
final step. In your driver's Init section, get the selector of
any function which will reside in the upper segment, and call
DevHlp Lock with that selector. Be sure to use the long-term
lock.
fptr = (PFUNCTION) SomeFunction;
codesel = SELECTOROF(fptr);
// lock the second code segment down permanently
if(LockSeg(
codesel, // selector
1, // lock long term
0, // wait for seg lock
(PLHANDLE) &lock_seg_han_code)) // handle returned
return (RPDONE | RPERR | ERROR_GEN_FAILURE);
return (RPDONE);
Sample Code 3. Locking the extra code segment
Your device header requires offsets to the strategy and IDC
routines, so declare them as NEAR before the header, or use
another method of your choice.
Need more data space for those large buffers? No problem. Use
the /ND option on the compiler command line to rename the data
segments (for example, DATA2). Pick the name of a variable in
the high data segment, get a pointer to it, extract the
selector, and call LockSeg with the selector.
cl -c -Alfu -Gs /NT_TEXT /ND_DATA2 -G2 -Zl -Zp yourfile.c
LIBRARY YOURLIB
PROTMODE
SEGMENTS
_DATA CLASS'DATA' PRELOAD
CONST CLASS'CONST' PRELOAD
_BSS CLASS'BSS' PRELOAD
FAR_BSS CLASS'FAR_BSS' PRELOAD
_TEXT CLASS'CODE' PRELOAD
_TEXT2 CLASS'CODE' PRELOAD IOPL
RMCode CLASS'CODE' PRELOAD IOPL
_DATA2 CLASS'DATA' PRELOAD IOPL
Sample Code 4. .DEF file for extra data segment
That's all there is to it. Well, almost--remember to mark your
new data segment as IOPL.
Just because you've got all this new space, remember to use it
sparingly!
Note: I write most of my device drivers in the small model,
using Microsoft C 6.0.
Steve Mastrianni is an Industry Consultant specializing in device
drivers and real-time applications for OS/2. The author of
Writing OS/2 2.1 Device Drivers in C, Steve is regarded as one of
the industry's leading experts in OS/2 and OS/2 device drivers.
Steve can be reached on CompuServe@73354,746, or Internet at
stevemas@vnet.ibm.com.