1 /* The kernel call implemented in this file:
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)
12 #include "kernel/system.h"
14 #include <minix/endpoint.h>
18 static int generic_handler(irq_hook_t
*hook
);
20 /*===========================================================================*
22 *===========================================================================*/
23 int do_irqctl(struct proc
* caller
, message
* m_ptr
)
25 /* Dismember the request message. */
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
;
38 /* See what is requested and take needed actions. */
39 switch(m_ptr
->m_lsys_krn_sys_irqctl
.request
) {
41 /* Enable or disable IRQs. This is straightforward. */
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
]);
51 disable_irq(&irq_hooks
[irq_hook_id
]);
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.
59 /* Check if IRQ line is acceptable. */
60 if (irq_vec
< 0 || irq_vec
>= NR_IRQ_VECTORS
) return(EINVAL
);
65 printf("do_irqctl: no priv structure!\n");
68 if (privp
->s_flags
& CHECK_IRQ
)
70 for (i
= 0; i
<privp
->s_nr_irq
; i
++)
72 if (irq_vec
== privp
->s_irq_tab
[i
])
75 if (i
>= privp
->s_nr_irq
)
78 "do_irqctl: IRQ check failed for proc %d, IRQ %d\n",
79 caller
->p_endpoint
, irq_vec
);
84 /* When setting a policy, the caller must provide an identifier that
85 * is returned on the notification message if a interrupt occurs.
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
);
90 /* Try to find an existing mapping to override. */
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
) {
96 hook_ptr
= &irq_hooks
[irq_hook_id
]; /* existing hook */
97 rm_irq_handler(&irq_hooks
[irq_hook_id
]);
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
) {
105 hook_ptr
= &irq_hooks
[irq_hook_id
]; /* free hook */
108 if (hook_ptr
== NULL
) return(ENOSPC
);
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
));
118 /* Return index of the IRQ hook in use. */
119 m_ptr
->m_krn_lsys_sys_irqctl
.hook_id
= irq_hook_id
+ 1;
123 if (irq_hook_id
< 0 || irq_hook_id
>= NR_IRQ_HOOKS
||
124 irq_hooks
[irq_hook_id
].proc_nr_e
== NONE
) {
126 } else if (caller
->p_endpoint
!= irq_hooks
[irq_hook_id
].proc_nr_e
) {
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
;
135 r
= EINVAL
; /* invalid IRQ REQUEST */
140 /*===========================================================================*
142 *===========================================================================*/
143 static int generic_handler(irq_hook_t
* hook
)
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.
151 /* As a side-effect, the interrupt handler gathers random information by
152 * timestamping the interrupt events. This is used for /dev/random.
154 get_randomness(&krandom
, hook
->irq
);
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.
160 if(!isokendpt(hook
->proc_nr_e
, &proc_nr
))
161 panic("invalid interrupt handler: %d", hook
->proc_nr_e
);
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
167 priv(proc_addr(proc_nr
))->s_int_pending
|= (1 << hook
->notify_id
);
169 /* Build notification message and return. */
170 mini_notify(proc_addr(HARDWARE
), hook
->proc_nr_e
);
171 return(hook
->policy
& IRQ_REENABLE
);
174 #endif /* USE_IRQCTL */