;*********************************************************************
;*                       Origin and Purpose                          *
;*                                                                   *
;* All changes I made are hereby in the Public Domain.  Commodore    *
;* retains its copyright, if any, of the original code in the Rom    *
;* Kernel manuals since this is a derivative work.  -Jeff Rush       *
;*                                                                   *
;*********************************************************************
;*                           History                                 *
;*                                                                   *
;* 20Sep89 jrr Original crib from Amiga Rom Kernel Manual.           *
;* 23Jul90 jrr Extensive descriptive prose added.                    *
;*                                                                   *
;*********************************************************************
        TITLE   Example Device Driver for Tutorial Use
        SMALLOBJ    ; Use PC-Relative Addressing
        OPTIMON     ; Enable C.A.P.E. 68K Optimizations

        SECTION TheOnlySection

        ;************
        ;* Includes *
        ;************
        NOLIST
        INCLUDE "exec/types.i"
        INCLUDE "exec/devices.i"
        INCLUDE "exec/initializers.i"
        INCLUDE "exec/memory.i"
        INCLUDE "exec/resident.i"
        INCLUDE "exec/io.i"
        INCLUDE "exec/ables.i"
        INCLUDE "exec/errors.i"
        INCLUDE "exec/tasks.i"
        INCLUDE "exec/semaphores.i"
        INCLUDE "hardware/intbits.i"

        INCLUDE "asmsupp.i"

        INCLUDE "Example.i"     ; Device-Specific Definitions
        LIST

        ;******************
        ;* Public Symbols *
        ;******************
        XDEF    CmdInvalid      ; Handle an Unknown or Out-of-Range Command
        XDEF    CmdReset        ; Reset the Device
        XDEF    CmdRead         ; Read Data from the Device for Caller
        XDEF    CmdWrite        ; Write Data into the Device from Caller
        XDEF    CmdUpdate       ; Force Outbound Data from Caches onto Device
        XDEF    CmdClear        ; Discard All Pending Unread Data inside Device
        XDEF    CmdStop         ; Suspend Device Operations
        XDEF    CmdStart        ; Resume Device Operations
        XDEF    CmdFlush        ; Discard All Queued but Unprocessed I/O Requests

        ;********************
        ;* External Symbols *
        ;********************
        ; This Name is for Debugging Use, to Annotate Serial-Debug Output
        IFNE    INFO_LEVEL      ; If Any Debugging Enabled at All
        XREF    subSysName
        ENDC

        ; Defined in Ex_Support.Asm
        XREF    TermIO          ; End of I/O Operation Exit Point

;*********************************************************************
;*                   'Invalid' Command Routine                       *
;*                                                                   *
;* This command causes the device to reply with an error of          *
;* IOERR_NOCMD as defined in Exec/Errors.h indicating that the       *
;* command is not supported.                                         *
;*                                                                   *
;* Inputs:                                                           *
;*   A6 - Ptr to our Device node.                                    *
;*   A1 - Ptr to the caller's I/O request block.                     *
;*   A3 - Ptr to the Unit Descriptor                                 *
;*                                                                   *
;* Outputs:                                                          *
;*   IO_ERROR  - Error code of operation.                            *
;*                                                                   *
;*********************************************************************
CmdInvalid:
        MOVE.B  #IOERR_NOCMD,IO_ERROR(A1)   ; Plug-In Error Code
        BRA     TermIO                      ; Send Request Back to User

;*********************************************************************
;*                    'Reset' Command Routine                        *
;*                                                                   *
;* This command causes the device to abort all I/O requests, both    *
;* those queued and in process and reset itself to its initialized   *
;* state.                                                            *
;*                                                                   *
;* Inputs:                                                           *
;*   A6 - Ptr to our Device node.                                    *
;*   A1 - Ptr to the caller's I/O request block.                     *
;*   A3 - Ptr to the Unit Descriptor                                 *
;*                                                                   *
;* Outputs:                                                          *
;*   IO_ERROR  - Zero if the reset succeeded,                        *
;*               an error code otherwise.                            *
;*                                                                   *
;*********************************************************************
CmdReset:
        CLR.L   IO_ACTUAL(A1)               ; Zero Actual Count
        BRA     TermIO                      ; Send Request Back to User

;*********************************************************************
;*                     'Read' Command Routine                        *
;*                                                                   *
;* This command causes the device to return data read from the       *
;* hardware to the caller.                                           *
;*                                                                   *
;* Inputs:                                                           *
;*   A6 - Ptr to our Device node.                                    *
;*   A1 - Ptr to the caller's I/O request block.                     *
;*   A3 - Ptr to the Unit Descriptor                                 *
;*                                                                   *
;* Outputs:                                                          *
;*   IO_ERROR  - Error code of operation.                            *
;*                                                                   *
;*********************************************************************
CmdRead:
        IFGE    INFO_LEVEL-200
        MOVE.L  IO_LENGTH(A1),-(SP)
        PUTMSG  200,<'%s/Rd len %ld'>
        ADDQ.L  #4,SP
        ENDC

        MOVEM.L A2/A3,-(SP)         ; Preserve Original Registers Across Function
        ;
        ; Check Ptr to User's Data Buffer for Word Alignment
        BTST.B  #0,IO_DATA(A1)      ; Is Data Buffer on an Odd Boundary?
        BNE.S   CmdRead_LenErr      ; Yes, Error!

        ;
        ; Perform Additional Checks and Calculations using IO_OFFSET and IO_LENGTH

        ;
        ; Perform Actual I/O Operation and Update IO_ACTUAL

CmdRead_End:
        MOVEM.L (SP)+,A2/A3         ; Restore Original Registers
        BRA     TermIO              ; Send Request Back to User

CmdRead_LenErr:
        MOVE.B  #IOERR_BADLENGTH,IO_ERROR(A2)   ; Signal an Error
        CLR.L   IO_ACTUAL(A2)       ; Report That No Data was Moved
        BRA.S   CmdRead_End

;*********************************************************************
;*                    'Write' Command Routine                        *
;*                                                                   *
;* This command causes the device to take data supplied by the       *
;* caller and send it to the hardware.                               *
;*                                                                   *
;* Inputs:                                                           *
;*   A6 - Ptr to our Device node.                                    *
;*   A1 - Ptr to the caller's I/O request block.                     *
;*   A3 - Ptr to the Unit Descriptor                                 *
;*                                                                   *
;* Outputs:                                                          *
;*   IO_ERROR  - Error code of operation.                            *
;*                                                                   *
;*********************************************************************
CmdWrite:
        IFGE    INFO_LEVEL-200
        MOVE.L  IO_LENGTH(A1),-(SP)
        PUTMSG  200,<'%s/Wr len %ld'>
        ADDQ.L  #4,SP
        ENDC

        MOVEM.L A2/A3,-(SP)         ; Preserve Original Registers Across Function
        ;
        ; Check Ptr to User's Data Buffer for Word Alignment
        BTST.B  #0,IO_DATA(A1)      ; Is Data Buffer on an Odd Boundary?
        BNE.S   CmdWrite_LenErr     ; Yes, Error!

        ;
        ; Perform Additional Checks and Calculations using IO_OFFSET and IO_LENGTH

        ;
        ; Perform Actual I/O Operation and Update IO_ACTUAL

CmdWrite_End:
        MOVEM.L (SP)+,A2/A3         ; Restore Original Registers
        BRA     TermIO              ; Send Request Back to User

CmdWrite_LenErr:
        MOVE.B  #IOERR_BADLENGTH,IO_ERROR(A2)   ; Signal an Error
        CLR.L   IO_ACTUAL(A2)       ; Report That No Data was Moved
        BRA.S   CmdWrite_End

;*********************************************************************
;*                    'Update' Command Routine                       *
;*                                                                   *
;* This command causes the device to send any data waiting in an     *
;* internal cache to the actual hardware.  It does not return until  *
;* all data is sent.                                                 *
;*                                                                   *
;* Inputs:                                                           *
;*   A6 - Ptr to our Device node.                                    *
;*   A1 - Ptr to the caller's I/O request block.                     *
;*   A3 - Ptr to the Unit Descriptor                                 *
;*                                                                   *
;* Outputs:                                                          *
;*   IO_ERROR  - Error code of operation.                            *
;*                                                                   *
;*********************************************************************
CmdUpdate:
        CLR.L   IO_ACTUAL(A1)               ; Zero Actual Count
        BRA     TermIO                      ; Send Request Back to User

;*********************************************************************
;*                    'Clear' Command Routine                        *
;*                                                                   *
;* This command causes the device to dump all pending unread data    *
;* packets on the floor.  All data available for reading is lost.    *
;*                                                                   *
;* Inputs:                                                           *
;*   A6 - Ptr to our Device node.                                    *
;*   A1 - Ptr to the caller's I/O request block.                     *
;*   A3 - Ptr to the Unit Descriptor                                 *
;*                                                                   *
;* Outputs:                                                          *
;*   IO_ERROR  - Error code of operation.                            *
;*                                                                   *
;*********************************************************************
CmdClear:
        CLR.L   IO_ACTUAL(A1)               ; Zero Actual Count
        BRA     TermIO                      ; Send Request Back to User

;*********************************************************************
;*                    'Stop' Command Routine                         *
;*                                                                   *
;* This command suspends the processing of I/O requests in the       *
;* queue, until a 'Start' command is received.  The 'Stop' command   *
;* is not stackable; e.g. no matter how many stops have been issued  *
;* it only takes one 'Start' to restart processing.                  *
;*                                                                   *
;* Inputs:                                                           *
;*   A6 - Ptr to our Device node.                                    *
;*   A1 - Ptr to the caller's I/O request block.                     *
;*   A3 - Ptr to the Unit Descriptor                                 *
;*                                                                   *
;* Outputs:                                                          *
;*   IO_ERROR  - Error code of operation.                            *
;*                                                                   *
;*********************************************************************
CmdStop:
        PUTMSG  30,<'%s/MyStop: called'>
        BSET    #MDUB_STOPPED,UNIT_FLAGS(A3)
        BRA     TermIO                      ; Send Request Back to User

;*********************************************************************
;*                    'Start' Command Routine                        *
;*                                                                   *
;* This command restarts or unsuspends the processing of queued I/O  *
;* requests.                                                         *
;*                                                                   *
;* Inputs:                                                           *
;*   A6 - Ptr to our Device node.                                    *
;*   A1 - Ptr to the caller's I/O request block.                     *
;*   A3 - Ptr to the Unit Descriptor                                 *
;*                                                                   *
;* Outputs:                                                          *
;*   IO_ERROR  - Error code of operation.                            *
;*                                                                   *
;*********************************************************************
CmdStart:
        PUTMSG  30,<'%s/Start: called'>
        BSR     InternalStart
        BRA     TermIO                  ; Send Request Back to User

InternalStart:
        MOVE.L  A1,-(SP)                ; Preserve Original A1 Across Function

        BCLR    #MDUB_STOPPED,UNIT_FLAGS(A3) ; Turn Processing Back On
;
;!!!!! What if there is no unit task for this unit???

        ;
        ; Kick the Unit Task to Start It Moving Again
        MOVE.B  MP_SIGBIT(A3),D1
        CLEAR   D0
        BSET    D1,D0                   ; Prepare a Signal Mask
        MOVE.L  MP_SIGTASK(A3),A1       ; Pickup Task to Signal
        LINKSYS Signal,md_SysLib(A6)    ; Issue Signal to Unit

        MOVE.L  (SP)+,A1                ; Restore Original A1
        RTS

;*********************************************************************
;*                    'Flush' Command Routine                        *
;*                                                                   *
;* This command causes the device to dump all pending I/O requests   *
;* currently queued for the device on the floor.  All data that      *
;* would have been written is lost and all such I/O requests are     *
;* aborted and returned to their issuer.  It does -not- affect I/O   *
;* requests actually in progress.                                    *
;*                                                                   *
;* Inputs:                                                           *
;*   A6 - Ptr to our Device node.                                    *
;*   A1 - Ptr to the caller's I/O request block.                     *
;*   A3 - Ptr to the Unit Descriptor                                 *
;*                                                                   *
;* Outputs:                                                          *
;*   IO_ERROR  - Error code of operation.                            *
;*                                                                   *
;* Notes:                                                            *
;*   Some funny magic goes on with the STOPPED bit in here.  The     *
;*   'Stop' command is defined as not being reentrant.  We therefore *
;*   save the old state of the bit and then restore it later.  This  *
;*   keeps us from needing to Disable() in 'Flush'.  It also fails   *
;*   miserably if someone does a start in the middle of a flush.     *
;*                                                                   *
;*********************************************************************
CmdFlush:
        PUTMSG  30,<'%s/Flush: called'>
        MOVEM.L D2/A1/A6,-(SP)  ; Preserve Original Registers

        MOVE.L  md_SysLib(A6),A6 ; Pickup Ptr to Exec Library

        BSET    #MDUB_STOPPED,UNIT_FLAGS(A3)    ; Stop Unit
        SNE     D2              ; Save Stop-Status in D2 until Later
Flush_Loop:
        MOVE.L  A3,A0           ; Pickup Ptr to Message Port at Front of Unit Descriptor
        CALLSYS GetMsg          ; Steal a Message from the Unit Task's Message Port
        TST.L   D0              ; Did We Obtain a Message?
        BEQ.S   Flush_End       ; Nope, Flush is Finished

        MOVE.L  D0,A1           ; Prepare to Abort I/O Request
        MOVE.B  #IOERR_ABORTED,IO_ERROR(A1) ; Set I/O Abort Error Code into Request
        CALLSYS ReplyMsg        ; Send Message Back to User

        BRA.S   Flush_Loop      ; Go Process Additional Messages
Flush_End:
        MOVE.L  D2,D0           ; Pickup Original Stop-Status in D0
        MOVEM.L (SP)+,D2/A1/A6  ; Restore Original Registers

        TST.B   D0              ; Was the Device Stopped When We Were Entered?
        BEQ.S   1$              ; Yes, Leave It Stopped

        BSR     InternalStart   ; No, Restart It Again
1$:
        BRA     TermIO          ; Send Request Back to User

;*********************************************************************
;*                                                                   *
;*********************************************************************
        END

