page 60,132 ; SKELETON.ASM - A SKELETON DEVICE DRIVER V1.0 ; (C) COPYRIGHT 1994 G.KLEIN ; ;This file contains structures, sample code and information intended ; to aid in building a device driver. It is set up as a skeleton, i.e. ; hardly any working code is provided. Only the Initialisation command ; is implemented - it displays a signon message and the commandline ; it was loaded from (excluding the "device[high]=" part). Sample code ; is provided both to let the driver load as to let it unload itself ; from memory after the initialisation command is processed. There is ; also a bit of code showing how to implement the driver as an EXE file, ; with different code depending on weather it is loaded from config.sys ; or from the command prompt. ;Although the major part of this file contains structures and other info, ; is assembles to a "working" device driver. It can be loaded from ; config.sys, and displays a signon message. When run from the commandline, ; it also displays a (different) message. It has been tested with and ; assembles "out of the box" under TASM V2.0 and MASM V5.0. ;The contents of this file is copyrighted. It may be used freely for ; whatever purpose you see it fit. Although a big effort has been made ; to ensure all information in this file is correct, no guarantees can ; be given. Any comments you may have on this file or the (correctness of) ; the information therein are welcome. Here is how you can reach me: ; ; Fidonet: Gertjan Klein @ 2:280/901.14 ; Internet: gklein@hacktic.nl (Gertjan Klein) ; as of Sept. 1994: gklein@xs4all.nl (Gertjan Klein) ;#################################################################### ;Some structures describing the request headers for the various commands. ; In the comment field, a "<" means this is input to the command, and ">" ; means this is output from the command. It is indicated weather the ; command applies to block (B) or character (C) device drivers, or both ; (B/C). If a command is defined later then version 2.0, the DOS version ; in which it first appears is placed in parenthesis. This also applies ; to the fields in the request headers. rh0 struc ;0 - Initialisation (B/C) rh0_len db ? ; < Length of packet rh0_unit db ? ;Unused rh0_cmd db 0 ; < Device driver command rh0_stat dw ? ; > Status returned by device driver rh0_rsvd db 8 dup (?) ;Reserved rh0_units db ? ; > Number of units (B) rh0_break dd ? ; < End of driver memory (5.0+) ; > Break address (new end drvr mem) rh0_bpb dd ? ; < Pointer to cmdline (inc. name) ; > Pointer to BPB pointers array (B) rh0_drv_ltr db ? ; < First available drive (B, 3.0+) rh0_msgflag dw ? ; > 1 To make DOS show errors (5.0+) rh0 ends rh1 struc ;1 - Media Check (B) rh1_len db ? ; < Length of packet rh1_unit db ? ; < Unit code rh1_cmd db 1 ; < Device driver command rh1_stat dw ? ; > Status returned by device driver rh1_rsvd db 8 dup (?) ;Reserved rh1_media db ? ; < Current media descriptor rh1_md_stat db ? ; > Media status (changed/not/?) rh1_volID dd ? ; > Ptr to Volume ID (3.0+) rh1 ends rh2 struc ;2 - Get BPB (B) rh2_len db ? ; < Length of packet rh2_unit db ? ; < Unit code rh2_cmd db 2 ; < Device driver command rh2_stat dw ? ; > Status returned by device driver rh2_rsvd db 8 dup (?) ;Reserved rh2_media db ? ; < Current media descriptor rh2_buff dd ? ; < Pointer to buffer rh2_bpb dd ? ; > Pointer to BPB rh2 ends rh3 struc ;3 - IOCTL input (B/C) rh3_len db ? ; < Length of packet rh3_unit db ? ; < Unit code (B) rh3_cmd db 3 ; < Device driver command rh3_stat dw ? ; > Status returned by device driver rh3_rsvd db 8 dup (?) ;Reserved rh3_media db ? ;Unused rh3_buff dd ? ; < Pointer to buffer rh3_count dw ? ; < Transfer count (bytes) ; > Nr of bytes read rh3 ends rh4 struc ;4 - Input (B/C) rh4_len db ? ; < Length of packet rh4_unit db ? ; < Unit code (B) rh4_cmd db 4 ; < Device driver command rh4_stat dw ? ; > Status returned by device driver rh4_rsvd db 8 dup (?) ;Reserved rh4_media db ? ; < Media descriptor byte (B) rh4_buff dd ? ; < Pointer to data transfer area rh4_count dw ? ; < Transfer count (B:sectors,C:bytes) ; > Nr of sectors/bytes read rh4_start dw ? ; < Start sector number (B) rh4_volID dd ? ; > Pointer to volume ID (B, 5.0+) rh4_HugeStart dd ? ; < 32-bits start sector (B, 5.0+) ; (if rh4_count = 0FFFFh) rh4 ends rh5 struc ;5 - Non-destructive input (C) rh5_len db ? ; < Length of packet rh5_unit db ? ;Unused rh5_cmd db 5 ; < Device driver command rh5_stat dw ? ; > Status returned by device driver rh5_rsvd db 8 dup (?) ;Reserved rh5_char db ? ; > Character returned rh5 ends rh6 struc ;6 - Input status (C) rh6_len db ? ; < Length of packet rh6_unit db ? ;Unused rh6_cmd db 6 ; < Device driver command rh6_stat dw ? ; > Status returned by device driver rh6_rsvd db 8 dup (?) ;Reserved rh6 ends rh7 struc ;7 - Input flush (C) rh7_len db ? ; < Length of packet rh7_unit db ? ;Unused rh7_cmd db 7 ; < Device driver command rh7_stat dw ? ; > Status returned by device driver rh7_rsvd db 8 dup (?) ;Reserved rh7 ends rh8 struc ;8 - Output (B/C) rh8_len db ? ; < Length of packet rh8_unit db ? ; < Unit code (B) rh8_cmd db 8 ; < Device driver command rh8_stat dw ? ; > Status returned by device driver rh8_rsvd db 8 dup (?) ;Reserved rh8_media db ? ; < Media descriptor byte (B) rh8_buff dd ? ; < Pointer to data transfer area rh8_count dw ? ; < Transfer count (B:sectors,C:bytes) ; > Nr of sectors/bytes read rh8_start dw ? ; < Start sector number (B) rh8_volID dd ? ; > Pointer to volume ID (B, 5.0+) rh8_HugeStart dd ? ; < 32-bits start sector (B, 5.0+) ; (if rh8_count = 0FFFFh) rh8 ends rh9 struc ;9 - Output with verify (B/C) rh9_len db ? ; < Length of packet rh9_unit db ? ; < Unit code (B) rh9_cmd db 9 ; < Device driver command rh9_stat dw ? ; > Status returned by device driver rh9_rsvd db 8 dup (?) ;Reserved rh9_media db ? ; < Media descriptor byte (B) rh9_buff dd ? ; < Pointer to data transfer area rh9_count dw ? ; < Transfer count (B:sectors,C:bytes) ; > Nr of sectors/bytes read rh9_start dw ? ; < Start sector number (B) rh9_volID dd ? ; > Pointer to volume ID (B, 5.0+) rh9_HugeStart dd ? ; < 32-bits start sector (B, 5.0+) ; (if rh9_count = 0FFFFh) rh9 ends rh0A struc ;0Ah - Output status (C) rh0A_len db ? ; < Length of packet rh0A_unit db ? ;Unused rh0A_cmd db 0ah ; < Device driver command rh0A_stat dw ? ; > Status returned by device driver rh0A_rsvd db 8 dup (?) ;Reserved rh0A ends rh0B struc ;0Bh - Output flush (C) rh0B_len db ? ; < Length of packet rh0B_unit db ? ;Unused rh0B_cmd db 0bh ; < Device driver command rh0B_stat dw ? ; > Status returned by device driver rh0B_rsvd db 8 dup (?) ;Reserved rh0B ends rh0C struc ;0Ch - IOCTL output (B/C) rh0C_len db ? ; < Length of packet rh0C_unit db ? ; < Unit code (B) rh0C_cmd db 0ch ; < Device driver command rh0C_stat dw ? ; > Status returned by device driver rh0C_rsvd db 8 dup (?) ;Reserved rh0C_media db ? ;Unused rh0C_buff dd ? ; < Pointer to buffer rh0C_count dw ? ; < Transfer count (bytes) ; > Nr of bytes written rh0C ends rh0D struc ;0Dh - Open (B/C) (3.0+) rh0D_len db ? ; < Length of packet rh0D_unit db ? ; < Unit code (B) rh0D_cmd db 0dh ; < Device driver command rh0D_stat dw ? ; > Status returned by device driver rh0D_rsvd db 8 dup (?) ;Reserved rh0D ends rh0E struc ;0Eh - Close (B/C) (3.0+) rh0E_len db ? ; < Length of packet rh0E_unit db ? ; < Unit code (B) rh0E_cmd db 0eh ; < Device driver command rh0E_stat dw ? ; > Status returned by device driver rh0E_rsvd db 8 dup (?) ;Reserved rh0E ends rh0F struc ;0Fh - Removable media (B) (3.0+) rh0F_len db ? ; < Length of packet rh0F_unit db ? ; < Unit code rh0F_cmd db 0fh ; < Device driver command rh0F_stat dw ? ; > Status returned by device driver rh0F_rsvd db 8 dup (?) ;Reserved rh0F ends rh10 struc ;10h - Output until busy (C) (3.0+) rh10_len db ? ; < Length of packet rh10_unit db ? ;Unused rh10_cmd db 10h ; < Device driver command rh10_stat dw ? ; > Status returned by device driver rh10_rsvd db 8 dup (?) ;Reserved rh10_media db ? ;Unused rh10_buff dd ? ; < Pointer to buffer rh10_count dw ? ; < Transfer count (B:sectors,C:bytes) ; > Nr of sectors/bytes written rh10 ends ;Commands 11h and 12h are undefined rh13 struc ;13h - Generic IOCTL (3.2+:B, 3.3+:B/C) rh13_len db ? ; < Length of packet rh13_unit db ? ; < Unit code (B) rh13_cmd db 13h ; < Device driver command rh13_stat dw ? ; > Status returned by device driver rh13_rsvd db 8 dup (?) ;Reserved rh13_major db ? ; < Category rh13_minor db ? ; < Minor function rh13_SI dw ? ; < Contents of SI register rh13_DI dw ? ; < Contents of DI register rh13_pkt dd ? ; < Pointer to generic IOCTL req. rh13 ends ;Commands 14 to 16 are undefined rh17 struc ;17h - Get device (B) (3.2+) rh17_len db ? ; < Length of packet rh17_unit db ? ; < Unit number to check ; > Last active drive (or 0) rh17_cmd db 17h ; < Device driver command rh17_stat dw ? ; > Status returned by device driver rh17_rsvd db 8 dup (?) ;Reserved rh17 ends rh18 struc ;18h - Set device (B) (3.2+) rh18_len db ? ; < Length of packet rh18_unit db ? ; < Unit to make active rh18_cmd db 18h ; < Device driver command rh18_stat dw ? ; > Status returned by device driver rh18_rsvd db 8 dup (?) ;Reserved rh18 ends rh19 struc ;19h - IOCTL query (B/C) (5.0+) rh19_len db ? ; < Length of packet rh19_unit db ? ; < Unit code (B) rh19_cmd db 19h ; < Device driver command rh19_stat dw ? ; > Status returned by device driver rh19_rsvd db 8 dup (?) ;Reserved rh19_major db ? ; < Category rh19_minor db ? ; < Minor function rh19_SI dw ? ; < Contents of SI register rh19_DI dw ? ; < Contents of DI register rh19_pkt dd ? ; < Pointer to IOCTL query req. rh19 ends ;#################################################################### ;General equates: bptr equ byte ptr wptr equ word ptr dptr equ dword ptr os equ offset ;#################################################################### cseg SEGMENT PARA PUBLIC 'CODE' ASSUME cs:cseg nxt_dev dd -1 ;Pointer to next device driver in chain attr dw 8000h ;Attribute: character device strat dw strategy ;Address of strategy routine intr dw interrupt ;Address of interrupt routine dname db "NOTELEKS" ;Name of the device driver (C), or ; (1 byte:) number of devices (B) ;The attribute word is defined as follows: ; Bit 0: (C): 1 if driver is stdin ; 1: (C): 1 if driver is stdout ; (B): 1 if driver supports 32-bit sector addressing ; (commands 4, 8 and 9) ; 2: (C): 1 if driver is NUL device (only allowed for DOS's own driver) ; 3: (C): 1 if driver is clock device ; 4: (C): 1 if driver supports fast character I/O (int 29h) ; 5: undefined ; 6: (C/B): 0 if driver supports commands 17h and 18h and/or 13h ; (get/set logical drive and generic IOCTL) ; 7: (C/B): 1 if driver supports IOCTL query (command 19h) ; 8: undefined ; 9: undefined ; 10: undefined ; 11: (C/B): 1 if driver supports commands 0dh, 0eh and 0fh ; (Open/close device and removable media) ; 12: (C): 1 if driver supports output until busy (command 10h) ; (B): 1 if driver requires first FAT sector for build BPB ; (command 2) ; 13: undefined ; 14: (C/B): 1 if driver supports IOCTL read and write (commands 3 ; and 0ch) ; 15: (C): 1 if driver supports a character device ; (B): 0 if driver supports a block device ReqHeader label dword RhOffs dw ? ;Saved offset of request header RhSeg dw ? ;Saved segment of request header strategy: mov RhSeg,es ;Save request header segment mov RhOffs,bx ;Save request header offset retf ;Return to caller ;The following command table is initialised to the exit routines to ; use when the command is not implemented. In this skeleton, only Init ; is implemented. Change the pointers as you add routines. ;Normally, at least the following routines are required: ; For character device drivers: 0,4,5,6,7,8,9,0ah,0bh ; For block devices : 0,1,2,4,8,9 cmdtab dw Init ; 0 Initialisation command dw Exit_Done ; 1 Media_check dw Exit_Done ; 2 Build BPB dw Exit_UC ; 3 IOCtl in dw Exit_Done ; 4 Input dw Exit_Busy ; 5 Non-destructive input dw Exit_Done ; 6 Input status dw Exit_Done ; 7 Input flush dw Exit_Done ; 8 Output dw Exit_Done ; 9 Output with verify dw Exit_Done ;0Ah Output status dw Exit_Done ;0Bh Output flush dw Exit_UC ;0Ch IOCtl out dw Exit_Done ;0Dh Device open (3.0+) dw Exit_Done ;0Eh Device close (3.0+) dw Exit_UC ;0Fh Removable media (3.0+) dw Exit_UC ;10h Output till busy (3.0+) dw Exit_UC ;11h (Unused) dw Exit_UC ;12h (Unused) dw Exit_UC ;13h Generic IOCTL (3.2:B, 3.3+:B/C) dw Exit_UC ;14h (Unused) dw Exit_UC ;15h (Unused) dw Exit_UC ;16h (Unused) dw Exit_UC ;17h Get logical device (3.2+) dw Exit_UC ;18h Set logical device (3.2+) dw Exit_UC ;19h IOCTL query (5.0+) interrupt: push ax ;Save general purpose registers push bx push cx push dx push si push di push ds ;Save segment registers push es mov ax,cs ;Make DS point to us mov ds,ax ASSUME cs:cseg,ds:cseg les bx,[ReqHeader] ;Get ptr to request header in ES:BX mov al,es:[bx.rh0_cmd] ;Get command cmp al,19h ;Check if within range ja Exit_UC ; no: set unknown command bit and exit shl al,1 ; yes: make word index xor ah,ah mov di,ax ;Place it in index register jmp [cmdtab.di] ; and jump to apropriate routine Exit_Busy: or es:[bx.rh0_stat],200h jmp short Exit_Done Exit_UC: or es:[bx.rh0_stat],8003h ;Set error and unknown command bits Exit_Done: or es:[bx.rh0_stat],100h ;Set done bit exit: pop es ;Restore segment registers pop ds pop di ;Restore general purpose registers pop si pop dx pop cx pop bx pop ax retf ;Return to caller ;******************************************************************** ;This is a good place to insert routines and data that should stay resident. ; Anything that can be disposed of after initialisation should be placed ; after the break address, that is, after the Init routine. ;******************************************************************** Init: ;Display signon message: mov ah,3 ;Read cursor position xor bh,bh ; for video page 0 int 10h ;(BIOS) (Returned in dx) mov ax,1301h ;Write string in teletype mode mov bl,3 ; with attribute cyan mov cx,so_len ; length of string mov bp,offset signon ; start of string push cs ; in es:bp pop es int 10h ;(BIOS) ;Show commandline (including driver name): mov ah,3 ;Read cursor position xor bh,bh ; for video page 0 int 10h ;(BIOS) (Returned in dx) les bx,[ReqHeader] ;Get ptr to request header in ES:BX les bp,es:[bx.rh0_bpb] ;Get pointer to commandline in ES:BP mov di,bp ;Get pointer in ES:DI too mov al,0ah ;Terminating character of commandline mov cx,256 ;Search max 256 bytes repnz scasb ;Search for it mov cx,di ;Get pointer to end of string sub cx,bp ;Calculate length mov ax,1301h ;Write string in teletype mode mov bx,2 ; with attribute green int 10h ;(BIOS) les bx,[ReqHeader] ;Get ptr to request header back DoNotInstall: mov wptr es:[bx.rh0_break],0 ;Set zero break address mov wptr es:[bx.rh0_break+2],cs ;Set break address segment jmp Exit_Done Install: mov wptr es:[bx.rh0_break],os Init ;Set break address to Init mov wptr es:[bx.rh0_break+2],cs ;Set break address segment jmp Exit_Done ;******************************************************************** EXE: ;This is what gets executed when the device driver is ran as an EXE file. ; It can be safely deleted if the device driver is converted to a binary ; file (.SYS or .BIN), though the label in the END directive should be ; changed then. This code could be used to communicate with the resident ; device driver. mov ax,cs ;Make DS=CS mov ds,ax mov ah,9 ;DOS display string function mov dx,os emsg1 ;Message to display int 21h ;(DOS) mov ax,4c01h ;Back to DOS with errorlevel 1 int 21h ;(DOS) ;******************************************************************** signon db 13,10,"SKELETON - Skeleton device driver (C) 1994 G.Klein." db 13,10,"Commandline: " so_len equ $-signon emsg1 db "SKELETON - This program must be installed from config.sys." db 13,10,'$' cseg ENDS END EXE