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