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