;-------------------------------------------------------------------- ; DRIVER.SYS (a character device driver) ; Copyright (c) 1985 by Dan Rollins ; ; Simple device driver illustrates the fundamental structures and ; actions of a DOS 2.0 installable device driver. ; ; Creates TESTDEV, an input and output character device (pseudo-device). ; ; To install: Assemble, link, then process with: ; exe2bin driver driver.sys ; Then add this line to your CONFIG.SYS file: ; device=driver.sys ; Then reboot DOS and copy a file to TESTDEV ;======================================================================= ;---- equates for character device drivers ---- ;======================================================================= ;-- equates for the data structures used by this driver ;-- offsets from the start of the Request Header RH_LEN equ 0 ;byte RH_UNIT_CODE equ 1 ;byte RH_CMD_CODE equ 2 ;byte RH_STATUS equ 3 ;word ;--- Request Header offsets for fields when the INIT function is executed IN_UNIT_COUNT equ 13 ;byte IN_END_ADDR equ 14 ;dword IN_BPB_PTR equ 18 ;dword ;--- Request Header offsets for fields for INPUT or OUTPUT functions IO_MEDIA_DESC equ 13 ;byte IO_TRANS_ADDR equ 14 ;dword IO_DATA_COUNT equ 18 ;word IO_START_SECTOR equ 20 ;word ;--- equates for the bit-flags used in the attribute field of the device header CHAR_DEVICE equ 8000h ;bit 15: 1 for character device, 0 block device IOCTL equ 4000h ;bit 14: 1 if IOCTL system calls are supported NON_IBM_FORMAT equ 2000h ;bit 13: 1 if block device is non-IBM format ;bits 12 to 4 are reserved CLOCK equ 8 ;bit 3: 1 if this is the CLOCK$ device NUL equ 4 ;bit 2: 1 if this is the NUL device STO equ 2 ;bit 1: 1 if this is the Standard Output device STI equ 1 ;bit 0: 1 if this is the Standard Input device ;--- equates for the bit-flags used in the status record for each command ERR_FLAG equ 8000h ;bit 15: 1 if error occurred (see below) ;bits 14 to 10 reserved BUSY equ 0200h ;bit 9: 1 if device is busy DONE equ 0100h ;bit 8: set to 1 before exiting to caller ;--- the following values occupy bits 7 thru 0 when an error occurs -- WRITE_PROTECT equ 0 UNKNOWN_UNIT equ 1 DEV_NOT_READY equ 2 UNKNOWN_CMD equ 3 ;<-- this is the only error used by this device --- CRC_ERROR equ 4 BAD_LEN equ 5 SEEK_ERROR equ 6 UNKNOWN_MEDIA equ 7 SECTOR_NOT_FND equ 8 OUT_OF_PAPER equ 9 WRITE_FAULT equ 0ah READ_FAULT equ 0bh GENERAL_FAILURE equ 0ch ;=============================================================== ;----------- data and code of the file begins here ------------- ;=============================================================== dev_seg segment test_dev proc far assume cs:dev_seg, ds:dev_seg, es:dev_seg ;------------------------------ ; The following lines are the device header that must exist for ; every device. This file has only one device, and it works with ; character I/O. header: ;label for the start of this device driver next_dev_ptr dd -1 ;this is the last (only) device defined dev_attribute dw CHAR_DEVICE strategy_ptr dw strategy ;the proc that sets up for the functions interrupt_ptr dw interrupt ;the proc that handles function calls device_name db 'TESTDEV ' ;8-byte string of device name request_ptr dd ? ;STRATEGY stores ES:BX request header pointer here ;INTERRUPT retrieves it ; this table is used to jump to the procedure that is indicated ; by DOS in the command code (third byte of the request header) fn_table dw init_fn ; 0: initialization procedure dw media_check_fn ; 1: media check procedure dw build_bpb_fn ; 2: build Bios Parameter Block dw ioctl_in_fn ; 3: IOCTL Input dw input_fn ; 4: read from device dw nd_input_fn ; 5: Non-Destructive, no-wait read dw input_status_fn ; 6: return status of input device dw input_flush_fn ; 7: flush (clear) the input buffer dw output_fn ; 8: write to device dw output_verify_fn ; 9: write and verify dw output_status_fn ; 10: return status of output device dw output_flush_fn ; 11: flush (clear) the output buffer dw ioctl_out_fn ; 12: IOCTL Output ;The following messages are displayed by TESTDEV when DOS ;makes a request. CR equ 0dh LF equ 0ah fn0_msg db 'Initializing TESTDEV Driver ',CR,LF,'$' fn4_msg db 'Input request : $' fn8_msg db 'Output request : $' crlf db CR,LF,'$' ;============================================================= ; STRATEGY procedure ; Just saves the request header pointer for the INTERRUPT procedure strategy proc far assume cs:dev_seg mov cs:word ptr request_ptr,bx mov cs:word ptr request_ptr+2,es ret ;FAR return to DOS strategy endp ;============================================================= ; INTERRUPT procedure ; Processes the command indicated in the request header. interrupt proc far assume cs:dev_seg, ds:nothing, es:nothing push ds ;preserve all registers push es push ax push bx push cx push dx push di push si push cs pop ds ;DS addresses the program data area les bx,request_ptr ;get the pointer saved by STRATEGY mov ah,0 mov al,es:[bx+RH_CMD_CODE] ;fetch the command mov di,ax shl di,1 ; * 2 to point to jump vector jmp fn_table[di] ;invoke that function procedure ;--- all command jumps except INIT, INPUT, and OUTPUT will end up here ---- nd_input_fn: ;requests for any of these commands media_check_fn: ; will result in an error message build_bpb_fn: ioctl_in_fn: input_status_fn: input_flush_fn: output_verify_fn: output_status_fn: output_flush_fn: ioctl_out_fn: error_exit: or word ptr es:[bx+RH_STATUS],ERR_FLAG ;set the ERROR_FLAG or word ptr es:[bx+RH_STATUS],UNKNOWN_CMD ;identify the error ;--- service request has been handled. ;--- Set the "done flag" and return to DOS. common_exit: or word ptr es:[bx+RH_STATUS],DONE ;always indicate completion pop si ; by setting the DONE bit pop di pop dx pop cx pop bx pop ax pop es pop ds ret ;FAR return ;-------------------------- ; INTERRUPT procedure function calls ; Only three types of requests are handled by TESTDEV. All other ; requests are routed through the ERROR_EXIT. ;------------------------------------------------------------------------- ; INPUT_FN ; Reads (waits for) 1 character from the keyboard, and displays it, ; then returns it (within the I/O structure) to DOS. input_fn proc near mov ch,0 mov cl,es:[bx+IO_DATA_COUNT] ;get number of bytes jcxz if_exit ;if no bytes, just exit if_10: mov dx,offset fn4_msg ;"Input request: " mov ah,9 ;DOS print string service int 21H mov ah,0 ;ROM-BIOS AWAIT_KEY function int 16H ;get AL = ASCII character, AH = scan code mov word ptr es:[bx+IO_DATA_COUNT],1 ;store the character count push ds lds di,es:[bx+IO_TRANS_ADDR] ;get SEG:OFF of destination mov [di],al ;store byte in destination pop ds mov dl,al mov ah,2 ;display the character int 21H mov dx,offset crlf ;start a new line mov ah,9 ;invoke DOS print string service int 21h loop if_10 ;do again, for multi-byte requests if_exit: jmp common_exit input_fn endp ;-------------------------------------------------------------------- ; OUTPUT_FN sends the character passed to it from DOS out to the ; standard output device. output_fn proc near mov ch,0 mov cl,es:[bx+IO_DATA_COUNT] ;get number of bytes jcxz of_exit ;if no bytes, just exit of_10: mov dx,offset fn8_msg ;"Output request :" mov ah,9 ;DOS print string service int 21H push ds lds si,es:[bx+IO_TRANS_ADDR] ;get SEG:OFF of data mov dl,[si] ;retrieve the character pop ds ;unstack or die mov ah,2 ;display the character int 21H mov dx,offset crlf ;print new line characters mov ah,9 ;DOS print string service int 21H loop of_10 ;get next (if multi-byte request) of_exit: jmp common_exit output_fn endp ;-------------------------, ; INIT_FN procedure ; Passes the end-of-driver address back to DOS. ; Note that the address of this procedure is passed back to DOS as the ; address of the end of the driver. After one invocation, INIT_FN ; is never called again; thus, it doesn't need to be saved with the ; rest of the function procedures. init_fn proc near mov dx,offset fn0_msg ;"Initializing TESTDEV" mov ah,9 ;DOS print string service int 21H ;-- debugging tool: ;-- store the address so the code can be examined with DEBUG --- push ds mov ax,0 mov ds,ax mov word ptr ds:[04f0h],offset interrupt mov word ptr ds:[04f2h],cs pop ds ;-- header address (offset,segment) is stored at address 0000:04F0 ;-- which is known as the "intra-application communication area" ;-- specify the end of this driver and exit to DOS ------- mov word ptr es:[bx+IN_END_ADDR],offset init_fn mov word ptr es:[bx+IN_END_ADDR+2],cs jmp common_exit init_fn endp interrupt endp test_dev endp dev_seg ends end header ;must specify for EXE2BIN