1*433d6423SLionel Sambuc /* The kernel call implemented in this file: 2*433d6423SLionel Sambuc * m_type: SYS_IRQCTL 3*433d6423SLionel Sambuc * 4*433d6423SLionel Sambuc * The parameters for this kernel call are: 5*433d6423SLionel Sambuc * m_lsys_krn_sys_irqctl.request (control operation to perform) 6*433d6423SLionel Sambuc * m_lsys_krn_sys_irqctl.vector (irq line that must be controlled) 7*433d6423SLionel Sambuc * m_lsys_krn_sys_irqctl.policy (irq policy allows reenabling interrupts) 8*433d6423SLionel Sambuc * m_lsys_krn_sys_irqctl.hook_id (provides index to be returned on interrupt) 9*433d6423SLionel Sambuc * m_krn_lsys_sys_irqctl.hook_id (returns index of irq hook assigned at kernel) 10*433d6423SLionel Sambuc */ 11*433d6423SLionel Sambuc 12*433d6423SLionel Sambuc #include "kernel/kernel.h" 13*433d6423SLionel Sambuc #include "kernel/system.h" 14*433d6423SLionel Sambuc 15*433d6423SLionel Sambuc #include <minix/endpoint.h> 16*433d6423SLionel Sambuc 17*433d6423SLionel Sambuc #if USE_IRQCTL 18*433d6423SLionel Sambuc 19*433d6423SLionel Sambuc static int generic_handler(irq_hook_t *hook); 20*433d6423SLionel Sambuc 21*433d6423SLionel Sambuc /*===========================================================================* 22*433d6423SLionel Sambuc * do_irqctl * 23*433d6423SLionel Sambuc *===========================================================================*/ 24*433d6423SLionel Sambuc int do_irqctl(struct proc * caller, message * m_ptr) 25*433d6423SLionel Sambuc { 26*433d6423SLionel Sambuc /* Dismember the request message. */ 27*433d6423SLionel Sambuc int irq_vec; 28*433d6423SLionel Sambuc int irq_hook_id; 29*433d6423SLionel Sambuc int notify_id; 30*433d6423SLionel Sambuc int r = OK; 31*433d6423SLionel Sambuc int i; 32*433d6423SLionel Sambuc irq_hook_t *hook_ptr; 33*433d6423SLionel Sambuc struct priv *privp; 34*433d6423SLionel Sambuc 35*433d6423SLionel Sambuc /* Hook identifiers start at 1 and end at NR_IRQ_HOOKS. */ 36*433d6423SLionel Sambuc irq_hook_id = m_ptr->m_lsys_krn_sys_irqctl.hook_id - 1; 37*433d6423SLionel Sambuc irq_vec = m_ptr->m_lsys_krn_sys_irqctl.vector; 38*433d6423SLionel Sambuc 39*433d6423SLionel Sambuc /* See what is requested and take needed actions. */ 40*433d6423SLionel Sambuc switch(m_ptr->m_lsys_krn_sys_irqctl.request) { 41*433d6423SLionel Sambuc 42*433d6423SLionel Sambuc /* Enable or disable IRQs. This is straightforward. */ 43*433d6423SLionel Sambuc case IRQ_ENABLE: 44*433d6423SLionel Sambuc case IRQ_DISABLE: 45*433d6423SLionel Sambuc if (irq_hook_id >= NR_IRQ_HOOKS || irq_hook_id < 0 || 46*433d6423SLionel Sambuc irq_hooks[irq_hook_id].proc_nr_e == NONE) return(EINVAL); 47*433d6423SLionel Sambuc if (irq_hooks[irq_hook_id].proc_nr_e != caller->p_endpoint) return(EPERM); 48*433d6423SLionel Sambuc if (m_ptr->m_lsys_krn_sys_irqctl.request == IRQ_ENABLE) { 49*433d6423SLionel Sambuc enable_irq(&irq_hooks[irq_hook_id]); 50*433d6423SLionel Sambuc } 51*433d6423SLionel Sambuc else 52*433d6423SLionel Sambuc disable_irq(&irq_hooks[irq_hook_id]); 53*433d6423SLionel Sambuc break; 54*433d6423SLionel Sambuc 55*433d6423SLionel Sambuc /* Control IRQ policies. Set a policy and needed details in the IRQ table. 56*433d6423SLionel Sambuc * This policy is used by a generic function to handle hardware interrupts. 57*433d6423SLionel Sambuc */ 58*433d6423SLionel Sambuc case IRQ_SETPOLICY: 59*433d6423SLionel Sambuc 60*433d6423SLionel Sambuc /* Check if IRQ line is acceptable. */ 61*433d6423SLionel Sambuc if (irq_vec < 0 || irq_vec >= NR_IRQ_VECTORS) return(EINVAL); 62*433d6423SLionel Sambuc 63*433d6423SLionel Sambuc privp= priv(caller); 64*433d6423SLionel Sambuc if (!privp) 65*433d6423SLionel Sambuc { 66*433d6423SLionel Sambuc printf("do_irqctl: no priv structure!\n"); 67*433d6423SLionel Sambuc return EPERM; 68*433d6423SLionel Sambuc } 69*433d6423SLionel Sambuc if (privp->s_flags & CHECK_IRQ) 70*433d6423SLionel Sambuc { 71*433d6423SLionel Sambuc for (i= 0; i<privp->s_nr_irq; i++) 72*433d6423SLionel Sambuc { 73*433d6423SLionel Sambuc if (irq_vec == privp->s_irq_tab[i]) 74*433d6423SLionel Sambuc break; 75*433d6423SLionel Sambuc } 76*433d6423SLionel Sambuc if (i >= privp->s_nr_irq) 77*433d6423SLionel Sambuc { 78*433d6423SLionel Sambuc printf( 79*433d6423SLionel Sambuc "do_irqctl: IRQ check failed for proc %d, IRQ %d\n", 80*433d6423SLionel Sambuc caller->p_endpoint, irq_vec); 81*433d6423SLionel Sambuc return EPERM; 82*433d6423SLionel Sambuc } 83*433d6423SLionel Sambuc } 84*433d6423SLionel Sambuc 85*433d6423SLionel Sambuc /* When setting a policy, the caller must provide an identifier that 86*433d6423SLionel Sambuc * is returned on the notification message if a interrupt occurs. 87*433d6423SLionel Sambuc */ 88*433d6423SLionel Sambuc notify_id = m_ptr->m_lsys_krn_sys_irqctl.hook_id; 89*433d6423SLionel Sambuc if (notify_id > CHAR_BIT * sizeof(irq_id_t) - 1) return(EINVAL); 90*433d6423SLionel Sambuc 91*433d6423SLionel Sambuc /* Try to find an existing mapping to override. */ 92*433d6423SLionel Sambuc hook_ptr = NULL; 93*433d6423SLionel Sambuc for (i=0; !hook_ptr && i<NR_IRQ_HOOKS; i++) { 94*433d6423SLionel Sambuc if (irq_hooks[i].proc_nr_e == caller->p_endpoint 95*433d6423SLionel Sambuc && irq_hooks[i].notify_id == notify_id) { 96*433d6423SLionel Sambuc irq_hook_id = i; 97*433d6423SLionel Sambuc hook_ptr = &irq_hooks[irq_hook_id]; /* existing hook */ 98*433d6423SLionel Sambuc rm_irq_handler(&irq_hooks[irq_hook_id]); 99*433d6423SLionel Sambuc } 100*433d6423SLionel Sambuc } 101*433d6423SLionel Sambuc 102*433d6423SLionel Sambuc /* If there is nothing to override, find a free hook for this mapping. */ 103*433d6423SLionel Sambuc for (i=0; !hook_ptr && i<NR_IRQ_HOOKS; i++) { 104*433d6423SLionel Sambuc if (irq_hooks[i].proc_nr_e == NONE) { 105*433d6423SLionel Sambuc irq_hook_id = i; 106*433d6423SLionel Sambuc hook_ptr = &irq_hooks[irq_hook_id]; /* free hook */ 107*433d6423SLionel Sambuc } 108*433d6423SLionel Sambuc } 109*433d6423SLionel Sambuc if (hook_ptr == NULL) return(ENOSPC); 110*433d6423SLionel Sambuc 111*433d6423SLionel Sambuc /* Install the handler. */ 112*433d6423SLionel Sambuc hook_ptr->proc_nr_e = caller->p_endpoint; /* process to notify */ 113*433d6423SLionel Sambuc hook_ptr->notify_id = notify_id; /* identifier to pass */ 114*433d6423SLionel Sambuc hook_ptr->policy = m_ptr->m_lsys_krn_sys_irqctl.policy; /* policy for interrupts */ 115*433d6423SLionel Sambuc put_irq_handler(hook_ptr, irq_vec, generic_handler); 116*433d6423SLionel Sambuc DEBUGBASIC(("IRQ %d handler registered by %s / %d\n", 117*433d6423SLionel Sambuc irq_vec, caller->p_name, caller->p_endpoint)); 118*433d6423SLionel Sambuc 119*433d6423SLionel Sambuc /* Return index of the IRQ hook in use. */ 120*433d6423SLionel Sambuc m_ptr->m_krn_lsys_sys_irqctl.hook_id = irq_hook_id + 1; 121*433d6423SLionel Sambuc break; 122*433d6423SLionel Sambuc 123*433d6423SLionel Sambuc case IRQ_RMPOLICY: 124*433d6423SLionel Sambuc if (irq_hook_id < 0 || irq_hook_id >= NR_IRQ_HOOKS || 125*433d6423SLionel Sambuc irq_hooks[irq_hook_id].proc_nr_e == NONE) { 126*433d6423SLionel Sambuc return(EINVAL); 127*433d6423SLionel Sambuc } else if (caller->p_endpoint != irq_hooks[irq_hook_id].proc_nr_e) { 128*433d6423SLionel Sambuc return(EPERM); 129*433d6423SLionel Sambuc } 130*433d6423SLionel Sambuc /* Remove the handler and return. */ 131*433d6423SLionel Sambuc rm_irq_handler(&irq_hooks[irq_hook_id]); 132*433d6423SLionel Sambuc irq_hooks[irq_hook_id].proc_nr_e = NONE; 133*433d6423SLionel Sambuc break; 134*433d6423SLionel Sambuc 135*433d6423SLionel Sambuc default: 136*433d6423SLionel Sambuc r = EINVAL; /* invalid IRQ REQUEST */ 137*433d6423SLionel Sambuc } 138*433d6423SLionel Sambuc return(r); 139*433d6423SLionel Sambuc } 140*433d6423SLionel Sambuc 141*433d6423SLionel Sambuc /*===========================================================================* 142*433d6423SLionel Sambuc * generic_handler * 143*433d6423SLionel Sambuc *===========================================================================*/ 144*433d6423SLionel Sambuc static int generic_handler(hook) 145*433d6423SLionel Sambuc irq_hook_t *hook; 146*433d6423SLionel Sambuc { 147*433d6423SLionel Sambuc /* This function handles hardware interrupt in a simple and generic way. All 148*433d6423SLionel Sambuc * interrupts are transformed into messages to a driver. The IRQ line will be 149*433d6423SLionel Sambuc * reenabled if the policy says so. 150*433d6423SLionel Sambuc */ 151*433d6423SLionel Sambuc int proc_nr; 152*433d6423SLionel Sambuc 153*433d6423SLionel Sambuc /* As a side-effect, the interrupt handler gathers random information by 154*433d6423SLionel Sambuc * timestamping the interrupt events. This is used for /dev/random. 155*433d6423SLionel Sambuc */ 156*433d6423SLionel Sambuc get_randomness(&krandom, hook->irq); 157*433d6423SLionel Sambuc 158*433d6423SLionel Sambuc /* Check if the handler is still alive. 159*433d6423SLionel Sambuc * If it's dead, this should never happen, as processes that die 160*433d6423SLionel Sambuc * automatically get their interrupt hooks unhooked. 161*433d6423SLionel Sambuc */ 162*433d6423SLionel Sambuc if(!isokendpt(hook->proc_nr_e, &proc_nr)) 163*433d6423SLionel Sambuc panic("invalid interrupt handler: %d", hook->proc_nr_e); 164*433d6423SLionel Sambuc 165*433d6423SLionel Sambuc /* Add a bit for this interrupt to the process' pending interrupts. When 166*433d6423SLionel Sambuc * sending the notification message, this bit map will be magically set 167*433d6423SLionel Sambuc * as an argument. 168*433d6423SLionel Sambuc */ 169*433d6423SLionel Sambuc priv(proc_addr(proc_nr))->s_int_pending |= (1 << hook->notify_id); 170*433d6423SLionel Sambuc 171*433d6423SLionel Sambuc /* Build notification message and return. */ 172*433d6423SLionel Sambuc mini_notify(proc_addr(HARDWARE), hook->proc_nr_e); 173*433d6423SLionel Sambuc return(hook->policy & IRQ_REENABLE); 174*433d6423SLionel Sambuc } 175*433d6423SLionel Sambuc 176*433d6423SLionel Sambuc #endif /* USE_IRQCTL */ 177