irq.inc
mxlgv_KolibriDev/src/kernel/core/irq.inc
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2020. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License    ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$Revision: 7733 $


IRQ_RESERVED   = 56

IRQ_POOL_SIZE  = 48

uglobal

align 16
irqh_tab            rd sizeof.LHEAD * IRQ_RESERVED / 4

irqh_pool           rd sizeof.IRQH * IRQ_POOL_SIZE /4
next_irqh           rd 1

irq_active_set      rd (IRQ_RESERVED+31)/32
irq_failed          rd IRQ_RESERVED

endg

set_irq_active:
        mov     eax, ebp
        mov     ecx, ebp
        shr     ecx, 5
        and     eax, 31
        bts     [irq_active_set+ecx*4], eax
        ret

reset_irq_active:
        mov     eax, ebp
        mov     ecx, ebp
        shr     ecx, 5
        and     eax, 31
        btr     [irq_active_set+ecx*4], eax
        ret

align 4
init_irqs:

        mov     ecx, IRQ_RESERVED
        mov     edi, irqh_tab
@@:
        mov     eax, edi
        stosd
        stosd
        loop    @B

        mov     ecx, IRQ_POOL_SIZE-1
        mov     eax, irqh_pool+sizeof.IRQH
        mov     [next_irqh], irqh_pool
@@:
        mov     [eax-sizeof.IRQH], eax
        add     eax, sizeof.IRQH
        loop    @B

        mov     [eax-sizeof.IRQH], dword 0
        ret


align 4
proc attach_int_handler stdcall, irq:dword, handler:dword, user_data:dword
         locals
           .irqh dd ?
         endl

        DEBUGF  1, "K : Attach Interrupt %d Handler %x\n", [irq], [handler]

        and     [.irqh], 0

        push    ebx

        mov     ebx, [irq]          ;irq num
        test    ebx, ebx
        jz      .err

        cmp     ebx, IRQ_RESERVED
        jae     .err

        mov     edx, [handler]
        test    edx, edx
        jz      .err

        spin_lock_irqsave IrqsList

;allocate handler

        mov     ecx, [next_irqh]
        test    ecx, ecx
        jz      .fail

        mov     eax, [ecx]
        mov     [next_irqh], eax
        mov     [.irqh], ecx

        mov     [irq_failed+ebx*4], 0;clear counter

        mov     eax, [user_data]
        mov     [ecx+IRQH.handler], edx
        mov     [ecx+IRQH.data], eax
        and     [ecx+IRQH.num_ints], 0

        lea     edx, [irqh_tab+ebx*8]
        list_add_tail ecx, edx     ;clobber eax
        stdcall enable_irq, ebx

.fail:
        spin_unlock_irqrestore IrqsList
.err:
        pop     ebx
        mov     eax, [.irqh]
        ret

endp

if 0
align 4
proc get_int_handler stdcall, irq:dword

        mov     eax, [irq]
        cmp     eax, 15
        ja      .fail
        mov     eax, [irq_tab + 4 * eax]
        ret
.fail:
        xor     eax, eax
        ret
endp
end if


align 4
proc  detach_int_handler

        ret
endp


macro irq_serv_h [num] {
    forward
align 4
  .irq_#num :
        push    num
        jmp     .main
}

align 16
irq_serv:

rept 12 irqn:1  {irq_serv_h irqn}       ;  1--12
rept 18 irqn:14 {irq_serv_h irqn}       ; 14--31 (irq32 is vector 0x40)
rept 23 irqn:33 {irq_serv_h irqn}       ; 33--55

purge irq_serv_h

align 16
.main:
        save_ring3_context
        mov     ebp, [esp + 32]
        mov     bx, app_data;os_data
        mov     ds, bx
        mov     es, bx

        cmp     [v86_irqhooks+ebp*8], 0
        jnz     v86_irq

        call    set_irq_active

        lea     esi, [irqh_tab+ebp*8]   ; esi= list head
        mov     ebx, esi
.next:
        mov     ebx, [ebx+IRQH.list.next]; ebx= irqh pointer
        cmp     ebx, esi
        je      .done

        push    ebx                     ; FIX THIS
        push    edi
        push    esi

        push    [ebx+IRQH.data]
        call    [ebx+IRQH.handler]
        pop     ecx

        pop     esi
        pop     edi
        pop     ebx

        test    eax, eax
        jz      .next

        inc     [ebx+IRQH.num_ints]
        call    reset_irq_active
        jmp     .next

.done:
        call    reset_irq_active
        jnc     .exit

; There is at least one configuration with one device which generates IRQ
; that is not the same as it should be according to PCI config space.
; For that device, the handler is registered at wrong IRQ.
; As a workaround, when nobody acknowledges the generated IRQ,
; try to ask all other registered handlers; if some handler acknowledges
; the IRQ this time, relink it to the current IRQ list.
; To make this more reliable, for every handler keep number of times
; that it has acknowledged an IRQ, and assume that handlers with at least one
; acknowledged IRQ are registered properly.
; Note: this still isn't 100% correct, because two IRQs can fire simultaneously,
; the better way would be to find the correct IRQ, but I don't know how to do
; this in that case.
        cmp     ebp, 1
        jz      .fail
        push    ebp
        xor     ebp, ebp
.try_other_irqs:
        cmp     ebp, [esp]
        jz      .try_next_irq
        cmp     ebp, 1
        jz      .try_next_irq
        cmp     ebp, 6
        jz      .try_next_irq
        cmp     ebp, 12
        jz      .try_next_irq
        cmp     ebp, 14
        jz      .try_next_irq
        cmp     ebp, 15
        jz      .try_next_irq
        lea     esi, [irqh_tab+ebp*8]
        mov     ebx, esi
.try_next_handler:
        mov     ebx, [ebx+IRQH.list.next]
        cmp     ebx, esi
        je      .try_next_irq
        cmp     [ebx+IRQH.num_ints], 0
        jne     .try_next_handler
; keyboard handler acknowledges everything
        push    [ebx+IRQH.data]
        call    [ebx+IRQH.handler]
        pop     ecx
        test    eax, eax
        jz      .try_next_handler

.found_in_wrong_list:
        DEBUGF 1,'K : warning: relinking handler from IRQ%d to IRQ%d\n',\
                ebp, [esp]
        pop     ebp
        spin_lock_irqsave IrqsList
        list_del ebx
        lea     edx, [irqh_tab+ebp*8]
        list_add_tail ebx, edx
        spin_unlock_irqrestore IrqsList
        jmp     .exit

.try_next_irq:
        inc     ebp
        cmp     ebp, 16
        jb      .try_other_irqs
        pop     ebp

.fail:
        inc     [irq_failed+ebp*4]
.exit:

        mov     ecx, ebp
        call    irq_eoi

; IRQ handler could make some kernel thread ready; reschedule
        mov     bl, SCHEDULE_HIGHER_PRIORITY
        call    find_next_task
        jz      .return  ; if there is only one running process
        call    do_change_task
  .return:
        restore_ring3_context
        add     esp, 4
        iret

align 4
irqD:
        push    eax
        push    ecx
        xor     eax, eax
        out     0xf0, al
        mov     cl, 13
        call    irq_eoi
        pop     ecx
        pop     eax
        iret

