1433d6423SLionel Sambuc /* The kernel call implemented in this file:
2433d6423SLionel Sambuc * m_type: SYS_IRQCTL
3433d6423SLionel Sambuc *
4433d6423SLionel Sambuc * The parameters for this kernel call are:
5433d6423SLionel Sambuc * m_lsys_krn_sys_irqctl.request (control operation to perform)
6433d6423SLionel Sambuc * m_lsys_krn_sys_irqctl.vector (irq line that must be controlled)
7433d6423SLionel Sambuc * m_lsys_krn_sys_irqctl.policy (irq policy allows reenabling interrupts)
8433d6423SLionel Sambuc * m_lsys_krn_sys_irqctl.hook_id (provides index to be returned on interrupt)
9433d6423SLionel Sambuc * m_krn_lsys_sys_irqctl.hook_id (returns index of irq hook assigned at kernel)
10433d6423SLionel Sambuc */
11433d6423SLionel Sambuc
12433d6423SLionel Sambuc #include "kernel/system.h"
13433d6423SLionel Sambuc
14433d6423SLionel Sambuc #include <minix/endpoint.h>
15433d6423SLionel Sambuc
16433d6423SLionel Sambuc #if USE_IRQCTL
17433d6423SLionel Sambuc
18433d6423SLionel Sambuc static int generic_handler(irq_hook_t *hook);
19433d6423SLionel Sambuc
20433d6423SLionel Sambuc /*===========================================================================*
21433d6423SLionel Sambuc * do_irqctl *
22433d6423SLionel Sambuc *===========================================================================*/
do_irqctl(struct proc * caller,message * m_ptr)23433d6423SLionel Sambuc int do_irqctl(struct proc * caller, message * m_ptr)
24433d6423SLionel Sambuc {
25433d6423SLionel Sambuc /* Dismember the request message. */
26433d6423SLionel Sambuc int irq_vec;
27433d6423SLionel Sambuc int irq_hook_id;
28433d6423SLionel Sambuc int notify_id;
29433d6423SLionel Sambuc int r = OK;
30433d6423SLionel Sambuc int i;
31433d6423SLionel Sambuc irq_hook_t *hook_ptr;
32433d6423SLionel Sambuc struct priv *privp;
33433d6423SLionel Sambuc
34433d6423SLionel Sambuc /* Hook identifiers start at 1 and end at NR_IRQ_HOOKS. */
35433d6423SLionel Sambuc irq_hook_id = m_ptr->m_lsys_krn_sys_irqctl.hook_id - 1;
36433d6423SLionel Sambuc irq_vec = m_ptr->m_lsys_krn_sys_irqctl.vector;
37433d6423SLionel Sambuc
38433d6423SLionel Sambuc /* See what is requested and take needed actions. */
39433d6423SLionel Sambuc switch(m_ptr->m_lsys_krn_sys_irqctl.request) {
40433d6423SLionel Sambuc
41433d6423SLionel Sambuc /* Enable or disable IRQs. This is straightforward. */
42433d6423SLionel Sambuc case IRQ_ENABLE:
43433d6423SLionel Sambuc case IRQ_DISABLE:
44433d6423SLionel Sambuc if (irq_hook_id >= NR_IRQ_HOOKS || irq_hook_id < 0 ||
45433d6423SLionel Sambuc irq_hooks[irq_hook_id].proc_nr_e == NONE) return(EINVAL);
46433d6423SLionel Sambuc if (irq_hooks[irq_hook_id].proc_nr_e != caller->p_endpoint) return(EPERM);
47433d6423SLionel Sambuc if (m_ptr->m_lsys_krn_sys_irqctl.request == IRQ_ENABLE) {
48433d6423SLionel Sambuc enable_irq(&irq_hooks[irq_hook_id]);
49433d6423SLionel Sambuc }
50433d6423SLionel Sambuc else
51433d6423SLionel Sambuc disable_irq(&irq_hooks[irq_hook_id]);
52433d6423SLionel Sambuc break;
53433d6423SLionel Sambuc
54433d6423SLionel Sambuc /* Control IRQ policies. Set a policy and needed details in the IRQ table.
55433d6423SLionel Sambuc * This policy is used by a generic function to handle hardware interrupts.
56433d6423SLionel Sambuc */
57433d6423SLionel Sambuc case IRQ_SETPOLICY:
58433d6423SLionel Sambuc
59433d6423SLionel Sambuc /* Check if IRQ line is acceptable. */
60433d6423SLionel Sambuc if (irq_vec < 0 || irq_vec >= NR_IRQ_VECTORS) return(EINVAL);
61433d6423SLionel Sambuc
62433d6423SLionel Sambuc privp= priv(caller);
63433d6423SLionel Sambuc if (!privp)
64433d6423SLionel Sambuc {
65433d6423SLionel Sambuc printf("do_irqctl: no priv structure!\n");
66433d6423SLionel Sambuc return EPERM;
67433d6423SLionel Sambuc }
68433d6423SLionel Sambuc if (privp->s_flags & CHECK_IRQ)
69433d6423SLionel Sambuc {
70433d6423SLionel Sambuc for (i= 0; i<privp->s_nr_irq; i++)
71433d6423SLionel Sambuc {
72433d6423SLionel Sambuc if (irq_vec == privp->s_irq_tab[i])
73433d6423SLionel Sambuc break;
74433d6423SLionel Sambuc }
75433d6423SLionel Sambuc if (i >= privp->s_nr_irq)
76433d6423SLionel Sambuc {
77433d6423SLionel Sambuc printf(
78433d6423SLionel Sambuc "do_irqctl: IRQ check failed for proc %d, IRQ %d\n",
79433d6423SLionel Sambuc caller->p_endpoint, irq_vec);
80433d6423SLionel Sambuc return EPERM;
81433d6423SLionel Sambuc }
82433d6423SLionel Sambuc }
83433d6423SLionel Sambuc
84433d6423SLionel Sambuc /* When setting a policy, the caller must provide an identifier that
85433d6423SLionel Sambuc * is returned on the notification message if a interrupt occurs.
86433d6423SLionel Sambuc */
87433d6423SLionel Sambuc notify_id = m_ptr->m_lsys_krn_sys_irqctl.hook_id;
88433d6423SLionel Sambuc if (notify_id > CHAR_BIT * sizeof(irq_id_t) - 1) return(EINVAL);
89433d6423SLionel Sambuc
90433d6423SLionel Sambuc /* Try to find an existing mapping to override. */
91433d6423SLionel Sambuc hook_ptr = NULL;
92433d6423SLionel Sambuc for (i=0; !hook_ptr && i<NR_IRQ_HOOKS; i++) {
93433d6423SLionel Sambuc if (irq_hooks[i].proc_nr_e == caller->p_endpoint
94433d6423SLionel Sambuc && irq_hooks[i].notify_id == notify_id) {
95433d6423SLionel Sambuc irq_hook_id = i;
96433d6423SLionel Sambuc hook_ptr = &irq_hooks[irq_hook_id]; /* existing hook */
97433d6423SLionel Sambuc rm_irq_handler(&irq_hooks[irq_hook_id]);
98433d6423SLionel Sambuc }
99433d6423SLionel Sambuc }
100433d6423SLionel Sambuc
101433d6423SLionel Sambuc /* If there is nothing to override, find a free hook for this mapping. */
102433d6423SLionel Sambuc for (i=0; !hook_ptr && i<NR_IRQ_HOOKS; i++) {
103433d6423SLionel Sambuc if (irq_hooks[i].proc_nr_e == NONE) {
104433d6423SLionel Sambuc irq_hook_id = i;
105433d6423SLionel Sambuc hook_ptr = &irq_hooks[irq_hook_id]; /* free hook */
106433d6423SLionel Sambuc }
107433d6423SLionel Sambuc }
108433d6423SLionel Sambuc if (hook_ptr == NULL) return(ENOSPC);
109433d6423SLionel Sambuc
110433d6423SLionel Sambuc /* Install the handler. */
111433d6423SLionel Sambuc hook_ptr->proc_nr_e = caller->p_endpoint; /* process to notify */
112433d6423SLionel Sambuc hook_ptr->notify_id = notify_id; /* identifier to pass */
113433d6423SLionel Sambuc hook_ptr->policy = m_ptr->m_lsys_krn_sys_irqctl.policy; /* policy for interrupts */
114433d6423SLionel Sambuc put_irq_handler(hook_ptr, irq_vec, generic_handler);
115433d6423SLionel Sambuc DEBUGBASIC(("IRQ %d handler registered by %s / %d\n",
116433d6423SLionel Sambuc irq_vec, caller->p_name, caller->p_endpoint));
117433d6423SLionel Sambuc
118433d6423SLionel Sambuc /* Return index of the IRQ hook in use. */
119433d6423SLionel Sambuc m_ptr->m_krn_lsys_sys_irqctl.hook_id = irq_hook_id + 1;
120433d6423SLionel Sambuc break;
121433d6423SLionel Sambuc
122433d6423SLionel Sambuc case IRQ_RMPOLICY:
123433d6423SLionel Sambuc if (irq_hook_id < 0 || irq_hook_id >= NR_IRQ_HOOKS ||
124433d6423SLionel Sambuc irq_hooks[irq_hook_id].proc_nr_e == NONE) {
125433d6423SLionel Sambuc return(EINVAL);
126433d6423SLionel Sambuc } else if (caller->p_endpoint != irq_hooks[irq_hook_id].proc_nr_e) {
127433d6423SLionel Sambuc return(EPERM);
128433d6423SLionel Sambuc }
129433d6423SLionel Sambuc /* Remove the handler and return. */
130433d6423SLionel Sambuc rm_irq_handler(&irq_hooks[irq_hook_id]);
131433d6423SLionel Sambuc irq_hooks[irq_hook_id].proc_nr_e = NONE;
132433d6423SLionel Sambuc break;
133433d6423SLionel Sambuc
134433d6423SLionel Sambuc default:
135433d6423SLionel Sambuc r = EINVAL; /* invalid IRQ REQUEST */
136433d6423SLionel Sambuc }
137433d6423SLionel Sambuc return(r);
138433d6423SLionel Sambuc }
139433d6423SLionel Sambuc
140433d6423SLionel Sambuc /*===========================================================================*
141433d6423SLionel Sambuc * generic_handler *
142433d6423SLionel Sambuc *===========================================================================*/
generic_handler(irq_hook_t * hook)143*6077d1adSDr. Florian Grätz static int generic_handler(irq_hook_t * hook)
144433d6423SLionel Sambuc {
145433d6423SLionel Sambuc /* This function handles hardware interrupt in a simple and generic way. All
146433d6423SLionel Sambuc * interrupts are transformed into messages to a driver. The IRQ line will be
147433d6423SLionel Sambuc * reenabled if the policy says so.
148433d6423SLionel Sambuc */
149433d6423SLionel Sambuc int proc_nr;
150433d6423SLionel Sambuc
151433d6423SLionel Sambuc /* As a side-effect, the interrupt handler gathers random information by
152433d6423SLionel Sambuc * timestamping the interrupt events. This is used for /dev/random.
153433d6423SLionel Sambuc */
154433d6423SLionel Sambuc get_randomness(&krandom, hook->irq);
155433d6423SLionel Sambuc
156433d6423SLionel Sambuc /* Check if the handler is still alive.
157433d6423SLionel Sambuc * If it's dead, this should never happen, as processes that die
158433d6423SLionel Sambuc * automatically get their interrupt hooks unhooked.
159433d6423SLionel Sambuc */
160433d6423SLionel Sambuc if(!isokendpt(hook->proc_nr_e, &proc_nr))
161433d6423SLionel Sambuc panic("invalid interrupt handler: %d", hook->proc_nr_e);
162433d6423SLionel Sambuc
163433d6423SLionel Sambuc /* Add a bit for this interrupt to the process' pending interrupts. When
164433d6423SLionel Sambuc * sending the notification message, this bit map will be magically set
165433d6423SLionel Sambuc * as an argument.
166433d6423SLionel Sambuc */
167433d6423SLionel Sambuc priv(proc_addr(proc_nr))->s_int_pending |= (1 << hook->notify_id);
168433d6423SLionel Sambuc
169433d6423SLionel Sambuc /* Build notification message and return. */
170433d6423SLionel Sambuc mini_notify(proc_addr(HARDWARE), hook->proc_nr_e);
171433d6423SLionel Sambuc return(hook->policy & IRQ_REENABLE);
172433d6423SLionel Sambuc }
173433d6423SLionel Sambuc
174433d6423SLionel Sambuc #endif /* USE_IRQCTL */
175