xref: /minix3/minix/lib/libddekit/src/irq.c (revision 2d64210c1dbcd340904718f2d4e9e81adeab3c7d)
1433d6423SLionel Sambuc #include "common.h"
2433d6423SLionel Sambuc #include <ddekit/interrupt.h>
3433d6423SLionel Sambuc #include <ddekit/memory.h>
4187bd3cdSWojciech Zajac #include <ddekit/panic.h>
5433d6423SLionel Sambuc #include <ddekit/semaphore.h>
6433d6423SLionel Sambuc #include <ddekit/thread.h>
7433d6423SLionel Sambuc 
8433d6423SLionel Sambuc #ifdef DDEBUG_LEVEL_IRQ
9433d6423SLionel Sambuc #undef DDEBUG
10433d6423SLionel Sambuc #define DDEBUG DDEBUG_LEVEL_IRQ
11433d6423SLionel Sambuc #endif
12433d6423SLionel Sambuc 
13433d6423SLionel Sambuc #include "debug.h"
14433d6423SLionel Sambuc 
15433d6423SLionel Sambuc struct ddekit_irq_s {
16433d6423SLionel Sambuc 	int irq;
17187bd3cdSWojciech Zajac 	int notify_id;
18433d6423SLionel Sambuc 	int irq_hook;
19433d6423SLionel Sambuc 	int shared;
20433d6423SLionel Sambuc 	void(*thread_init)(void *);
21433d6423SLionel Sambuc 	void(*handler)(void *);
22433d6423SLionel Sambuc 	void *priv;
23433d6423SLionel Sambuc 	int enabled;
24433d6423SLionel Sambuc 	ddekit_thread_t *th;
25433d6423SLionel Sambuc 	ddekit_sem_t *sem;
26433d6423SLionel Sambuc 	struct ddekit_irq_s *next;
27433d6423SLionel Sambuc };
28433d6423SLionel Sambuc 
29433d6423SLionel Sambuc static struct ddekit_irq_s *irqs = 0;
30433d6423SLionel Sambuc static ddekit_lock_t lock;
31187bd3cdSWojciech Zajac static int next_notify_id = 0; /* TODO: This is only incremented and after
32187bd3cdSWojciech Zajac 				* enough interrupt attachments/detachments
33187bd3cdSWojciech Zajac 				* we can run out of legal IDs (this is however
34187bd3cdSWojciech Zajac 				* very atypical use case) */
35433d6423SLionel Sambuc 
36433d6423SLionel Sambuc /******************************************************************************
37433d6423SLionel Sambuc  *    Local helpers                                                           *
38433d6423SLionel Sambuc  *****************************************************************************/
39433d6423SLionel Sambuc 
40433d6423SLionel Sambuc 
41433d6423SLionel Sambuc static void ddekit_irq_lock(void);
42433d6423SLionel Sambuc static void ddekit_irq_unlock(void);
43433d6423SLionel Sambuc static struct ddekit_irq_s* find_by_irq(int irq);
44187bd3cdSWojciech Zajac static struct ddekit_irq_s* find_by_irq_id(int irq_id);
45433d6423SLionel Sambuc static void ddekit_irq_remove(struct ddekit_irq_s *irq_s);
46433d6423SLionel Sambuc static void ddekit_irq_thread(void *data);
47433d6423SLionel Sambuc 
48433d6423SLionel Sambuc /******************************************************************************
49433d6423SLionel Sambuc  *       ddekit_irq_lock                                                      *
50433d6423SLionel Sambuc  *****************************************************************************/
ddekit_irq_lock(void)51433d6423SLionel Sambuc static void  ddekit_irq_lock(void)
52433d6423SLionel Sambuc {
53433d6423SLionel Sambuc 	ddekit_lock_lock(&lock);
54433d6423SLionel Sambuc }
55433d6423SLionel Sambuc 
56433d6423SLionel Sambuc /******************************************************************************
57433d6423SLionel Sambuc  *       ddekit_irq_unlock                                                    *
58433d6423SLionel Sambuc  *****************************************************************************/
ddekit_irq_unlock(void)59433d6423SLionel Sambuc static void ddekit_irq_unlock(void)
60433d6423SLionel Sambuc {
61433d6423SLionel Sambuc 	ddekit_lock_unlock(&lock);
62433d6423SLionel Sambuc }
63433d6423SLionel Sambuc 
64433d6423SLionel Sambuc /******************************************************************************
65433d6423SLionel Sambuc  *       find_by_irq                                                          *
66433d6423SLionel Sambuc  *****************************************************************************/
find_by_irq(int irq)67433d6423SLionel Sambuc static struct ddekit_irq_s * find_by_irq(int irq)
68433d6423SLionel Sambuc {
69433d6423SLionel Sambuc 	struct ddekit_irq_s * irq_s;
70433d6423SLionel Sambuc 	ddekit_irq_lock();
71433d6423SLionel Sambuc 	if (!irqs) {
72433d6423SLionel Sambuc 		return 0;
73433d6423SLionel Sambuc 	}
74433d6423SLionel Sambuc 
75433d6423SLionel Sambuc 	irq_s = irqs;
76433d6423SLionel Sambuc 
77433d6423SLionel Sambuc 	while(irq_s) {
78433d6423SLionel Sambuc 		if(irq_s->irq==irq)
79433d6423SLionel Sambuc 			break;
80433d6423SLionel Sambuc 		irq_s = irq_s->next;
81433d6423SLionel Sambuc 	}
82433d6423SLionel Sambuc 
83433d6423SLionel Sambuc 	ddekit_irq_unlock();
84433d6423SLionel Sambuc 	return irq_s;
85433d6423SLionel Sambuc }
86433d6423SLionel Sambuc 
87433d6423SLionel Sambuc /******************************************************************************
88187bd3cdSWojciech Zajac  *       find_by_irq_id                                                       *
89187bd3cdSWojciech Zajac  *****************************************************************************/
find_by_irq_id(int irq_id)90187bd3cdSWojciech Zajac static struct ddekit_irq_s * find_by_irq_id(int irq_id)
91187bd3cdSWojciech Zajac {
92187bd3cdSWojciech Zajac 	struct ddekit_irq_s * irq_s;
93187bd3cdSWojciech Zajac 	ddekit_irq_lock();
94187bd3cdSWojciech Zajac 	if (!irqs) {
95187bd3cdSWojciech Zajac 		return 0;
96187bd3cdSWojciech Zajac 	}
97187bd3cdSWojciech Zajac 
98187bd3cdSWojciech Zajac 	irq_s = irqs;
99187bd3cdSWojciech Zajac 
100187bd3cdSWojciech Zajac 	while(irq_s) {
101187bd3cdSWojciech Zajac 		if(irq_s->notify_id==irq_id)
102187bd3cdSWojciech Zajac 			break;
103187bd3cdSWojciech Zajac 		irq_s = irq_s->next;
104187bd3cdSWojciech Zajac 	}
105187bd3cdSWojciech Zajac 
106187bd3cdSWojciech Zajac 	ddekit_irq_unlock();
107187bd3cdSWojciech Zajac 	return irq_s;
108187bd3cdSWojciech Zajac }
109187bd3cdSWojciech Zajac 
110187bd3cdSWojciech Zajac /******************************************************************************
111433d6423SLionel Sambuc  *       ddekit_irq_remove                                                    *
112433d6423SLionel Sambuc  *****************************************************************************/
ddekit_irq_remove(struct ddekit_irq_s * irq_s)113433d6423SLionel Sambuc static void ddekit_irq_remove(struct ddekit_irq_s *irq_s)
114433d6423SLionel Sambuc {
115433d6423SLionel Sambuc 	struct ddekit_irq_s *i;
116433d6423SLionel Sambuc 
117433d6423SLionel Sambuc 	ddekit_irq_lock();
118433d6423SLionel Sambuc 
119433d6423SLionel Sambuc 	if(!irqs) {
120433d6423SLionel Sambuc 		ddekit_irq_unlock();
121433d6423SLionel Sambuc 		return;
122433d6423SLionel Sambuc 	}
123433d6423SLionel Sambuc 
124433d6423SLionel Sambuc 	if(irqs==irq_s) {
125433d6423SLionel Sambuc 		irqs=irq_s->next;
126433d6423SLionel Sambuc 		ddekit_irq_unlock();
127433d6423SLionel Sambuc 		return;
128433d6423SLionel Sambuc 	}
129433d6423SLionel Sambuc 
130433d6423SLionel Sambuc 	i = irqs;
131433d6423SLionel Sambuc 
132433d6423SLionel Sambuc 	while(i) {
133433d6423SLionel Sambuc 		if (i->next == irq_s)  {
134433d6423SLionel Sambuc 			i->next = irq_s->next;
135433d6423SLionel Sambuc 			ddekit_irq_unlock();
136433d6423SLionel Sambuc 			return;
137433d6423SLionel Sambuc 		}
138433d6423SLionel Sambuc 		i = i->next;
139433d6423SLionel Sambuc 	}
140433d6423SLionel Sambuc 
141433d6423SLionel Sambuc 	ddekit_irq_unlock();
142433d6423SLionel Sambuc }
143433d6423SLionel Sambuc 
144433d6423SLionel Sambuc /******************************************************************************
145433d6423SLionel Sambuc  *       ddekit_irq_thread                                                    *
146433d6423SLionel Sambuc  *****************************************************************************/
ddekit_irq_thread(void * data)147433d6423SLionel Sambuc static void ddekit_irq_thread(void *data)
148433d6423SLionel Sambuc {
149433d6423SLionel Sambuc 	/* For each IRQ line an own thread is started */
150433d6423SLionel Sambuc 
151433d6423SLionel Sambuc 	struct ddekit_irq_s *irq_s = (struct ddekit_irq_s *) data;
152433d6423SLionel Sambuc 
153433d6423SLionel Sambuc 	/* call IRQ thread init function */
154433d6423SLionel Sambuc 	irq_s->thread_init(irq_s->priv);
155433d6423SLionel Sambuc 
156433d6423SLionel Sambuc 	while(1) {
157433d6423SLionel Sambuc 
158433d6423SLionel Sambuc 		/* Wait for IRQs */
159433d6423SLionel Sambuc 		DDEBUG_MSG_VERBOSE("wating for IRQ %d to occur", irq_s->irq);
160433d6423SLionel Sambuc 		ddekit_sem_down(irq_s->sem);
161433d6423SLionel Sambuc 		DDEBUG_MSG_VERBOSE("executing handler for IRQ %d", irq_s->irq);
162433d6423SLionel Sambuc 		irq_s->handler(irq_s->priv);
163433d6423SLionel Sambuc 
164433d6423SLionel Sambuc 	}
165433d6423SLionel Sambuc }
166433d6423SLionel Sambuc 
167433d6423SLionel Sambuc 
168433d6423SLionel Sambuc /******************************************************************************
169433d6423SLionel Sambuc  *   DDEKIT public API (include/dde/ddekit)                                   *
170433d6423SLionel Sambuc  *****************************************************************************/
171433d6423SLionel Sambuc 
172433d6423SLionel Sambuc /******************************************************************************
173433d6423SLionel Sambuc  *       ddekit_interrupt_attach                                              *
174433d6423SLionel Sambuc  *****************************************************************************/
ddekit_interrupt_attach(int irq,int shared,void (* thread_init)(void *),void (* handler)(void *),void * priv)175433d6423SLionel Sambuc ddekit_thread_t *ddekit_interrupt_attach(int irq,
176433d6423SLionel Sambuc                                          int shared,
177433d6423SLionel Sambuc                                          void(*thread_init)(void *),
178433d6423SLionel Sambuc                                          void(*handler)(void *),
179433d6423SLionel Sambuc                                          void *priv)
180433d6423SLionel Sambuc {
181433d6423SLionel Sambuc 	struct ddekit_irq_s *irq_s;
182187bd3cdSWojciech Zajac 	int err_code;
183433d6423SLionel Sambuc 	char name[32];
184433d6423SLionel Sambuc 	irq_s = (struct ddekit_irq_s *)
185433d6423SLionel Sambuc 	                ddekit_simple_malloc(sizeof(struct ddekit_irq_s));
186433d6423SLionel Sambuc 
187433d6423SLionel Sambuc 	irq_s->sem         = ddekit_sem_init(0);
188187bd3cdSWojciech Zajac 	irq_s->irq         = irq;		/* represents real IRQ number */
189187bd3cdSWojciech Zajac 	ddekit_irq_lock();
190187bd3cdSWojciech Zajac 	irq_s->notify_id   = next_notify_id;	/* represents kernel's IRQ ID */
191187bd3cdSWojciech Zajac 	irq_s->irq_hook    = next_notify_id;	/* after given ID is passed to
192187bd3cdSWojciech Zajac 						 * kernel, this field will be
193187bd3cdSWojciech Zajac 						 * set to real irq_hook */
194187bd3cdSWojciech Zajac 	next_notify_id++;			/* next time, assign different
195187bd3cdSWojciech Zajac 						 * ID so we can distinguish
196187bd3cdSWojciech Zajac 						 * interrupts */
197187bd3cdSWojciech Zajac 	ddekit_irq_unlock();
198433d6423SLionel Sambuc 	irq_s->shared      = shared;
199433d6423SLionel Sambuc 	irq_s->thread_init = thread_init;
200433d6423SLionel Sambuc 	irq_s->handler     = handler;
201433d6423SLionel Sambuc 	irq_s->priv        = priv;
202433d6423SLionel Sambuc 	irq_s->next        = 0;
203433d6423SLionel Sambuc 	irq_s->enabled     = 1;
204433d6423SLionel Sambuc 
205433d6423SLionel Sambuc 	/* create interrupt thread */
206433d6423SLionel Sambuc 	snprintf(name, 32, "ddekit_irq_%d",irq);
207433d6423SLionel Sambuc 	irq_s->th = ddekit_thread_create(ddekit_irq_thread, irq_s, name);
208433d6423SLionel Sambuc 
209187bd3cdSWojciech Zajac 	/* try attaching to IRQ */
210187bd3cdSWojciech Zajac 	/* do not automatically re-enable interupts */
211187bd3cdSWojciech Zajac 	if (0 != (err_code = sys_irqsetpolicy(irq, 0, &irq_s->irq_hook)))
212187bd3cdSWojciech Zajac 		ddekit_panic("Failed to attach interrupt (ERROR %d)", err_code);
213433d6423SLionel Sambuc 
214433d6423SLionel Sambuc 	/* add to IRQ list */
215433d6423SLionel Sambuc 	ddekit_irq_lock();
216433d6423SLionel Sambuc 	irq_s->next = irqs;
217433d6423SLionel Sambuc 	irqs=irq_s;
218433d6423SLionel Sambuc 	ddekit_irq_unlock();
219433d6423SLionel Sambuc 
220433d6423SLionel Sambuc 	DDEBUG_MSG_INFO("Attached to irq %d (hook: %d)", irq, irq_s->irq_hook);
221433d6423SLionel Sambuc 
222433d6423SLionel Sambuc 	return irq_s->th;
223433d6423SLionel Sambuc }
224433d6423SLionel Sambuc 
225433d6423SLionel Sambuc /******************************************************************************
226433d6423SLionel Sambuc  *       ddekit_interrupt_detach                                              *
227433d6423SLionel Sambuc  *****************************************************************************/
ddekit_interrupt_detach(int irq)228433d6423SLionel Sambuc void ddekit_interrupt_detach(int irq)
229433d6423SLionel Sambuc {
230433d6423SLionel Sambuc 	struct ddekit_irq_s *irq_s;
231187bd3cdSWojciech Zajac 	int err_code;
232433d6423SLionel Sambuc 
233433d6423SLionel Sambuc 	irq_s = find_by_irq(irq);
234433d6423SLionel Sambuc 
235187bd3cdSWojciech Zajac 	if (0 != (err_code = sys_irqrmpolicy(&irq_s->irq_hook)))
236187bd3cdSWojciech Zajac 		ddekit_panic("Failed to detach interrupt (ERROR %d)", err_code);
237433d6423SLionel Sambuc 
238433d6423SLionel Sambuc 	ddekit_thread_terminate(irq_s->th);
239433d6423SLionel Sambuc 	ddekit_irq_remove(irq_s);
240433d6423SLionel Sambuc 	ddekit_simple_free(irq_s);
241433d6423SLionel Sambuc 	DDEBUG_MSG_VERBOSE(" IRQ %d", irq);
242433d6423SLionel Sambuc }
243433d6423SLionel Sambuc 
244433d6423SLionel Sambuc /******************************************************************************
245433d6423SLionel Sambuc  *       ddekit_interrupt_disable                                             *
246433d6423SLionel Sambuc  *****************************************************************************/
ddekit_interrupt_disable(int irq)247433d6423SLionel Sambuc void ddekit_interrupt_disable(int irq)
248433d6423SLionel Sambuc {
249433d6423SLionel Sambuc 	struct ddekit_irq_s *irq_s;
250433d6423SLionel Sambuc 	irq_s = find_by_irq(irq);
251433d6423SLionel Sambuc 	irq_s->enabled=0;
252433d6423SLionel Sambuc 	//sys_irqdisable(&irq_s->irq_hook);
253433d6423SLionel Sambuc 	DDEBUG_MSG_VERBOSE(" IRQ %d", irq);
254433d6423SLionel Sambuc }
255433d6423SLionel Sambuc 
256433d6423SLionel Sambuc /******************************************************************************
257433d6423SLionel Sambuc  *       ddekit_interrupt_enable                                              *
258433d6423SLionel Sambuc  *****************************************************************************/
ddekit_interrupt_enable(int irq)259433d6423SLionel Sambuc void ddekit_interrupt_enable(int irq)
260433d6423SLionel Sambuc {
261433d6423SLionel Sambuc 	struct ddekit_irq_s *irq_s;
262433d6423SLionel Sambuc 	irq_s = find_by_irq(irq);
263433d6423SLionel Sambuc 	irq_s->enabled=1;
264433d6423SLionel Sambuc 	//sys_irqenable(&irq_s->irq_hook);
265433d6423SLionel Sambuc 	DDEBUG_MSG_VERBOSE(" IRQ %d", irq);
266433d6423SLionel Sambuc }
267433d6423SLionel Sambuc 
268433d6423SLionel Sambuc /******************************************************************************
269433d6423SLionel Sambuc  *       ddekit_init_irqs                                                     *
270433d6423SLionel Sambuc  *****************************************************************************/
ddekit_init_irqs()271433d6423SLionel Sambuc void ddekit_init_irqs()
272433d6423SLionel Sambuc {
273433d6423SLionel Sambuc 	ddekit_lock_init(&lock);
274433d6423SLionel Sambuc }
275433d6423SLionel Sambuc 
276433d6423SLionel Sambuc /******************************************************************************
277433d6423SLionel Sambuc  *   DDEKIT internals (src/irq.h)                                             *
278433d6423SLionel Sambuc  *****************************************************************************/
279433d6423SLionel Sambuc 
280433d6423SLionel Sambuc /******************************************************************************
281433d6423SLionel Sambuc  *       _ddekit_interrupt_trigger                                            *
282433d6423SLionel Sambuc  *****************************************************************************/
_ddekit_interrupt_trigger(int irq_id)283187bd3cdSWojciech Zajac void _ddekit_interrupt_trigger(int irq_id)
284433d6423SLionel Sambuc {
285433d6423SLionel Sambuc 	struct ddekit_irq_s *irq_s;
286187bd3cdSWojciech Zajac 	int err_code;
287433d6423SLionel Sambuc 
288187bd3cdSWojciech Zajac 	irq_s = find_by_irq_id(irq_id);
289433d6423SLionel Sambuc 
290433d6423SLionel Sambuc 	if (irq_s)	{
291*2d64210cSWojciech Zajac 		DDEBUG_MSG_VERBOSE("Triggering IRQ %d", irq_s->irq);
292433d6423SLionel Sambuc 		ddekit_sem_up(irq_s->sem);
293187bd3cdSWojciech Zajac 		if (0 != (err_code = sys_irqenable(&irq_s->irq_hook)))
294187bd3cdSWojciech Zajac 			ddekit_panic("Failed to enable interrupt "
295187bd3cdSWojciech Zajac 					"(ERROR %d)", err_code);
296433d6423SLionel Sambuc 	} else {
297*2d64210cSWojciech Zajac 		DDEBUG_MSG_WARN("no handler for IRQ %d", irq_s->irq);
298433d6423SLionel Sambuc 	}
299433d6423SLionel Sambuc }
300433d6423SLionel Sambuc 
301433d6423SLionel Sambuc 
302