        title   ATIMDVR -- Attached timer device driver example
        subttl  Copyright (c) Psion PLC 1991
        name    ATIMDVR
;
;  VER    DATE    BY  DESCRIPTION
; ----- -------- ---- -----------
; 1.00A 01/02/92 JH   Alpha release
;
TURBOC	equ	1
;
        include epocdef.inc
        include epocmac.inc
        include epocpan.inc
        include epoclib.inc
        include epocser.inc

HandlerDisable         equ     000h     ; the hnadler should not be called
HandlerEnable          equ     001h     ; the handler should be called
;
TimerEnt       struc
        TimerIo         ChanEnt <>              ; I/O device driver hdr
        ReadRq          RqEnt   <>              ; read request info
        ReadStat        dw      ?               ; read completion status
        InStrategy      dw      ?               ; internal control 
        WaitHandler     dw      ?               ; wait handler handle
        TimerChan       dw      ?               ; open timer channel handle
        Timeout         dd      ?               ; read timout
        TimerStat       dw      ?               ; timer completion status
TimerEnt       ends

        CodeSeg

        ProcBegin@ AttachedTimerLDD
;                 =================
; The externally loadable Attached timer device table
        dw      LDDSignature
        db      'ATM',0,0,0,0,0
        dw      (VectorEnd-Vector)/2
Vector:
        dw      AttachedTimerInstall
        dw      AttachedTimerRemove
        dw      AttachedTimerHold
        dw      AttachedTimerResume
        dw      AttachedTimerReset
        dw      AttachedTimerUnits
        dw      AttachedTimerOpen
        dw      AttachedTimerStrategy
VectorHandler:
        dw      AttachedTimerHandler
VectorEnd:
        ProcEnd noret


        ProcBegin@ AttachedTimerInstall,far
;                 =====================
; Called on device installation by the operating system
;
        clc                             ; installed OK
        ret
        ProcEnd noret


        ProcBegin@ AttachedTimerRemove,far
;                 ====================
; Always allow to be removed.
; 
        clc                             ; removed ok.
        ret
        ProcEnd noret


        ProcBegin@ AttachedTimerHold,far
;                 ==================
; This does not need to do anything.
;
        ret
        ProcEnd noret


        ProcBegin@ AttachedTimerResume,far
;                 ====================
; This does not need to do anything.
;
        ret
        ProcEnd noret


        ProcBegin@ AttachedTimerReset,far
;                 ===================
; This will never be called since we dont request it to be called.
; The lower level driver and timer driver will handle the process 
; terminating abnormally.
;
        ProcEnd noret


        ProcBegin@ AttachedTimerUnits,far
;                 ===================
; Called by device query units operating system service
;
        mov     ax, -1                  ; supports many open requests
        ret
        ProcEnd noret


        ProcBegin@ AttachedTimerOpen,far
;                 ==================
; Called by the IoOpen system service
;
; In:
;       DX - device handle
;       SI - ptr to OpenEnt struct
; Out:
;   Carry clear
;       BX - open channel handle
;       DX - device handle as passed
;   Carry set
;       AL - error number
;       DX - device handle as passed
;
        cld
        mov     cx, (size TimerEnt)
        HeapAllocateCell
        jc      endOpen         ; return that error
        mov     bx, ax

; Initialise the  allocated cell to zero.
        mov     di, ax
        shr     cx, 1           ; alloc cells should be even no of bytes.
        xor     ax, ax
        rep     stosw                   ; initialize all struct to zero

; Add the waithandler function 
        mov     al, (VectorHandler-Vector)/2
        IoAddHandler
        jc      freeCellExit            ; report the add handler error
        mov     [bx].WaitHandler, ax    ; handler handle

; Open a timer channel
        push    bx                      ; save the alloc cell handle
        mov     cx, sp                  ; save SP for now
        xor     ax, ax
        push    ax
        mov     ax, ':' shl 8 + 'M'
        push    ax
        mov     ax, 'I' shl 8 + 'T'
        push    ax
        mov     bx, sp                  ; BX is pointer to name to 'TIM:'
        IoOpen                          ; CX and DX are irrelvant for a timer
        mov     sp, cx
        pop     bx
        jc      freeHandlerExit         ; report the timer open error.
        mov     [bx].TimerChan, ax

; Set up the channel header
        mov     [bx].ChanSignature, IoChanSignature
        mov     [bx].ChanLibHandle, dx
        mov     [bx].ChanNext, bx       ; were a root driver for now

; Attach this driver to the currently open driver Hierarchy
        mov     cx, bx                  ; channel were attaching
        mov     al, IoFuncAttach
        mov     bx, [si].OpenChan       ; driver to attach to
        IoWithWait
        clc                             ; return BX (ie what attached to)

; Finished the open request
endOpen:
        ret

; An error has occured, free the handler then cell in BX and return an error
freeHandlerExit:
        push    ax
        push    bx
        mov     bx, [bx].WaitHandler
        IoRemoveHandler
        pop     bx
        pop     ax

; An error has occured, free the cell in BX and return an error
freeCellExit:
        push    ax
        HeapFreeCell
        pop     ax                      ; AL has the error code
        stc
        ret
        ProcEnd noret


        ProcBegin@ StrategyVectorTable
;                  ===================
; The strategy vector jump table
;
        dw      StrategyDefault         ; IoFuncPanic
        dw      StrategyRead            ; IoFuncRead
        dw      StrategyDefault         ; IoFuncWrite
        dw      StrategyClose           ; IoFuncClose
        dw      StrategyCancel          ; IoFuncCancel
        dw      StrategyDefault         ; IoFuncAttach
        dw      StrategyDefault         ; IoFuncDetach
        dw      StrategySet             ; IoFuncSet
        dw      StrategySense           ; IoFuncSense
StrategyVectorEnd:
        ProcEnd noret


        ProcBegin@ CancelTimer
;                  ===========
; Cancel the outstanding timer request
; In:
;       BX = TimerEnt ptr
; Out:
;       NONE
;
        push    bx
        mov     bx, [bx].TimerChan
        mov     al, IoFuncCancel        ; cancel the queued timer
        IoWithWait
        pop     bx
        lea     di, [bx].TimerStat
        IoWaitForStatus                 ; use up completion signal
        ret
        ProcEnd noret

        ProcBegin@ CancelRead
;                  ==========
; Cancel the outstanding read request on the lower driver
; In:
;       BX = TimerEnt ptr
; Out:
;       NONE
;
        mov     al, IoFuncCancel        ; cancel the queued read
        IoWithWait
        lea     di, [bx].ReadStat
        IoWaitForStatus                 ; use up completion signal
        ret
        ProcEnd noret

        ProcBegin@ SignalReadAX
;                  ============
; Complete the orignal read request with the completion status in AX
; In:
;       AX = completion status to report
;       BX = control block ptr as allocated by open
; Out:
;       NONE
;
        xor     di, di                  ; complete the original read request
        xchg    di, [bx].ReadRq.RqStatusPtr
        mov     word ptr [di], ax
        IoSignal
        ret
        ProcEnd noret

        ProcBegin@ CancelAnyRequests
;                  =================
; Cancel any outstanding attached driver and timer requests.
; In:
;       BX = control block ptr as allocated by open
; Out:
;       NONE
;
        cmp     [bx].ReadRq.RqStatusPtr, 0
        je      noRequestsQueued
        call    CancelRead
        call    CancelTimer
        push    bx
        mov     bx, [bx].WaitHandler
        mov     cl, HandlerDisable
        IoEnableHandler
        pop     bx
        mov     ax, CancelErr
        call    SignalReadAX

noRequestsQueued:
        ret
        ProcEnd noret

        ProcBegin@ AttachedTimerStrategy,far
;                  =====================
; Called by the applications I/O services.
; In:
;       BX = control block ptr as allocated by open
;       SI = RqEnt pointer
; Out:
;    Carry clear
;       The request was sucessfully queued, (may have completed)
;    Carry set
;       The request failed to start
;
        cld
        mov     [bx].InStrategy, 1              ; anti nesting 
        mov     di, [si].RqStatusPtr
        mov     word ptr [di], PendingErr       ; request now pending
        mov     ax, di                          ; pre-load AX
        mov     di, [si].RqFunction
        cmp     di, (StrategyVectorEnd-StrategyVectorTable)/2
        ja      StrategyDefault
        mov     cx, [si].RqA1Ptr                ; pre-load CX and DX
        mov     dx, [si].RqA2Ptr
        shl     di, 1                           ; vector table entry
        jmp     word ptr cs:StrategyVectorTable[di]

; This device driver does not handle this function, pass it up the driver
; hierarchy to a driver that may support this function.
StrategyDefault:
        mov     [bx].InStrategy, 0
        IoSuper                                 ; pass request to next dvr
        ret

StrategyRead:
        cmp     [bx].ReadRq.RqStatusPtr, 0
        jne     panicPending                    ; already a read queued
        mov     [bx].ReadRq.RqStatusPtr, ax
        mov     [bx].ReadRq.RqA1Ptr, cx
        mov     [bx].ReadRq.RqA2Ptr, dx
        lea     di, [bx].ReadStat               ; Q an attached dvr read
        mov     al, IoFuncRead
        IoAsynchronousNoError           ; Queue a read on lower dvr
        lea     di, [bx].TimerStat
        lea     cx, [bx].Timeout
        push    bx
        push    [bx].WaitHandler
        mov     bx, [bx].TimerChan
        mov     al, IoFuncRead
        IoAsynchronousNoError           ; Queue a read on the timer channel
; now let the wait handler be callable
        pop     bx
        mov     cl, HandlerEnable
        IoEnableHandler
        pop     bx
        and     [bx].InStrategy, 0
        clc
        ret

; The process is making a 2nd call of a function that is already outstanding.
panicPending:
        mov     al, PanicIoPending
        ProcPanic                               ; stop calling process.

StrategyCancel:
        call    CancelAnyRequests
        and     [bx].InStrategy, 0
        jmp     short completeRequestOk

; This driver defines the IoFuncSet function to set the read timout.
StrategySet:
        mov     di, cx                  ; RqA1Ptr is a pointer to a long
        mov     ax, [di]
        mov     word ptr [bx].Timeout, ax
        mov     ax, [di+2]
        mov     word ptr [bx+2].Timeout, ax
        and     [bx].InStrategy, 0
        jmp     short completeRequestOk

; This driver defines the IoFuncSense function to sense the read timout.
StrategySense:
        mov     di, cx                  ; RqA1Ptr is a pointer to a long
        mov     ax, word ptr [bx].Timeout
        mov     word ptr [di], ax
        mov     ax, word ptr [bx+2].Timeout
        mov     word ptr [di+2], ax
completeRequestOk:
        mov     di, [si].RqStatusPtr
        and     word ptr [di], 0                ; complete request OK.
        IoSignal
        and     [bx].InStrategy, 0
        clc
        ret

StrategyClose:
        call    CancelAnyRequests
        push    bx
        push    [bx].WaitHandler
        mov     bx, [bx].TimerChan
        IoClose                                 ; close the timer
        pop     bx
        IoRemoveHandler                         ; remove the waithandler
        pop     bx
        mov     al, IoFuncDetach                ; detach from lower driver
        IoWithWait
        HeapFreeCell                            ; alloc cell freed !!!
        mov     di, [si].RqStatusPtr
        and     word ptr [di], 0                ; complete request OK.
        IoSignal
        clc
        ret

        ProcEnd noret

        ProcBegin@ AttachedTimerHandler,far
;                  ====================
; Called by the operating system when the applications I/O semaphore has
; been signalled. See if its one of our requests that have been completed.
; The operating system will automatically stop any nesting if it called this
; handler, however we will get called if we are in the strategy vector and
; use synchronous I/O requests. The driver has set itself up such that this
; will only ever be called if there is an outstanding request by only 
; enabling the handler when a read request is received.
;
; In:
;       BX = as passed to IoAddHandler 
; Out:
;   Carry clear
;       The signal was not for us.
;   Carry set
;       AL = 0 - we used signal, allow handler to be disabled.
;       AL = 1 - we used signal, keep the handler enabled.
;

        cmp     [bx].InStrategy, 1
        jne     notInAttachedTimerStrategy
signalNotForThisDriver:
        clc                     ; get out as fast as possible     
        ret                     ; as event definatly not for us.

notInAttachedTimerStrategy:
        cmp     [bx].ReadStat, PendingErr
        je      readRequestNotComplete
; The read request has completed.
        call    CancelTimer
        mov     ax, [bx].ReadStat               ; read completion result
        jmp     short signalAXdisableHandler

readRequestNotComplete:
        cmp     [bx].TimerStat, PendingErr
        je      signalNotForThisDriver
; The timer request has completed.
        call    CancelRead
        mov     ax, InActivityErr               ; read inactivity on timeout
signalAXdisableHandler:
        call    SignalReadAX
        xor     al, al                          ; we used signal
        stc                                     ; handler now disabled.
        ret
        ProcEnd noret


        EndCodeSeg
;
stack   segment stack para 'data'
;
stack   ends
;
        end     AttachedTimerLDD
