xref: /minix3/minix/kernel/system/do_irqctl.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
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