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