Deleted Added
full compact
kern_intr.c (39680) kern_intr.c (41059)
1/*
2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
10 * disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
1/*
2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
10 * disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * $Id: kern_intr.c,v 1.19 1998/08/11 15:08:13 bde Exp $
26 * $Id: kern_intr.c,v 1.20 1998/09/26 14:25:31 dfr Exp $
27 *
28 */
29
30
27 *
28 */
29
30
31#include <sys/types.h>
32#include <sys/malloc.h>
31#include <sys/param.h>
33#include <sys/systm.h>
32#include <sys/systm.h>
33#include <sys/malloc.h>
34#include <sys/errno.h>
35#ifdef RESOURCE_CHECK
36#include <sys/drvresource.h>
37#endif /* RESOURCE_CHECK */
38
39#include <machine/ipl.h>
40
41#ifdef __i386__
42#include <i386/isa/icu.h>
43#include <i386/isa/intr_machdep.h>
44#endif
45
46#include <sys/interrupt.h>
47
48#include <stddef.h>
49
50#ifdef __i386__
51
52typedef struct intrec {
53 intrmask_t mask;
54 inthand2_t *handler;
55 void *argument;
56 struct intrec *next;
57 void *devdata;
58 int intr;
59 intrmask_t *maskptr;
60 int flags;
61} intrec;
62
63static intrec *intreclist_head[NHWI];
64
65#endif
66
67struct swilist {
68 swihand_t *sl_handler;
69 struct swilist *sl_next;
70};
71
72static struct swilist swilists[NSWI];
73
74#ifdef __i386__
75
76/*
77 * The interrupt multiplexer calls each of the handlers in turn,
78 * and applies the associated interrupt mask to "cpl", which is
79 * defined as a ".long" in /sys/i386/isa/ipl.s
80 */
81
82#ifndef SMP
83static __inline intrmask_t
84splq(intrmask_t mask)
85{
86 intrmask_t tmp = cpl;
87 cpl |= mask;
88 return (tmp);
89}
90#endif /* SMP */
91
92static void
93intr_mux(void *arg)
94{
95 intrec *p = arg;
96
97 while (p != NULL) {
98 int oldspl = splq(p->mask);
99 p->handler(p->argument);
100 splx(oldspl);
101 p = p->next;
102 }
103}
104
105static intrec*
106find_idesc(unsigned *maskptr, int irq)
107{
108 intrec *p = intreclist_head[irq];
109
110 while (p && p->maskptr != maskptr)
111 p = p->next;
112
113 return (p);
114}
115
116static intrec**
117find_pred(intrec *idesc, int irq)
118{
119 intrec **pp = &intreclist_head[irq];
120 intrec *p = *pp;
121
122 while (p != idesc) {
123 if (p == NULL)
124 return (NULL);
125 pp = &p->next;
126 p = *pp;
127 }
128 return (pp);
129}
130
131/*
132 * Both the low level handler and the shared interrupt multiplexer
133 * block out further interrupts as set in the handlers "mask", while
134 * the handler is running. In fact *maskptr should be used for this
135 * purpose, but since this requires one more pointer dereference on
136 * each interrupt, we rather bother update "mask" whenever *maskptr
137 * changes. The function "update_masks" should be called **after**
138 * all manipulation of the linked list of interrupt handlers hung
139 * off of intrdec_head[irq] is complete, since the chain of handlers
140 * will both determine the *maskptr values and the instances of mask
141 * that are fixed. This function should be called with the irq for
142 * which a new handler has been add blocked, since the masks may not
143 * yet know about the use of this irq for a device of a certain class.
144 */
145
146static void
147update_mux_masks(void)
148{
149 int irq;
150 for (irq = 0; irq < ICU_LEN; irq++) {
151 intrec *idesc = intreclist_head[irq];
152 while (idesc != NULL) {
153 if (idesc->maskptr != NULL) {
154 /* our copy of *maskptr may be stale, refresh */
155 idesc->mask = *idesc->maskptr;
156 }
157 idesc = idesc->next;
158 }
159 }
160}
161
162static void
163update_masks(intrmask_t *maskptr, int irq)
164{
165 intrmask_t mask = 1 << irq;
166
167 if (maskptr == NULL)
168 return;
169
170 if (find_idesc(maskptr, irq) == NULL) {
171 /* no reference to this maskptr was found in this irq's chain */
172 if ((*maskptr & mask) == 0)
173 return;
174 /* the irq was included in the classes mask, remove it */
175 INTRUNMASK(*maskptr, mask);
176 } else {
177 /* a reference to this maskptr was found in this irq's chain */
178 if ((*maskptr & mask) != 0)
179 return;
180 /* put the irq into the classes mask */
181 INTRMASK(*maskptr, mask);
182 }
183 /* we need to update all values in the intr_mask[irq] array */
184 update_intr_masks();
185 /* update mask in chains of the interrupt multiplex handler as well */
186 update_mux_masks();
187}
188
189/*
190 * Add interrupt handler to linked list hung off of intreclist_head[irq]
191 * and install shared interrupt multiplex handler, if necessary
192 */
193
194static int
195add_intrdesc(intrec *idesc)
196{
197 int irq = idesc->intr;
198
199 intrec *head = intreclist_head[irq];
200
201 if (head == NULL) {
202 /* first handler for this irq, just install it */
203 if (icu_setup(irq, idesc->handler, idesc->argument,
204 idesc->maskptr, idesc->flags) != 0)
205 return (-1);
206
207 update_intrname(irq, (intptr_t)idesc->devdata);
208 /* keep reference */
209 intreclist_head[irq] = idesc;
210 } else {
211 if ((idesc->flags & INTR_EXCL) != 0
212 || (head->flags & INTR_EXCL) != 0) {
213 /*
214 * can't append new handler, if either list head or
215 * new handler do not allow interrupts to be shared
216 */
217 if (bootverbose)
218 printf("\tdevice combination doesn't support "
219 "shared irq%d\n", irq);
220 return (-1);
221 }
222 if (head->next == NULL) {
223 /*
224 * second handler for this irq, replace device driver's
225 * handler by shared interrupt multiplexer function
226 */
227 icu_unset(irq, head->handler);
228 if (icu_setup(irq, (inthand2_t*)intr_mux, head, 0, 0) != 0)
229 return (-1);
230 if (bootverbose)
231 printf("\tusing shared irq%d.\n", irq);
232 update_intrname(irq, -1);
233 }
234 /* just append to the end of the chain */
235 while (head->next != NULL)
236 head = head->next;
237 head->next = idesc;
238 }
239 update_masks(idesc->maskptr, irq);
240 return (0);
241}
242
243/*
244 * Add the interrupt handler descriptor data structure created by an
245 * earlier call of create_intr() to the linked list for its irq and
246 * adjust the interrupt masks if necessary.
247 *
248 * This function effectively activates the handler.
249 */
250
251int
252intr_connect(intrec *idesc)
253{
254 int errcode = -1;
255 int irq;
256
257#ifdef RESOURCE_CHECK
258 int resflag;
259#endif /* RESOURCE_CHECK */
260
261 if (idesc == NULL)
262 return (-1);
263
264 irq = idesc->intr;
265#ifdef RESOURCE_CHECK
266 resflag = (idesc->flags & INTR_EXCL) ? RESF_NONE : RESF_SHARED;
267 if (resource_claim(idesc->devdata, REST_INT, resflag, irq, irq) == 0)
268#endif /* RESOURCE_CHECK */
269 {
270 /* block this irq */
271 intrmask_t oldspl = splq(1 << irq);
272
273 /* add irq to class selected by maskptr */
274 errcode = add_intrdesc(idesc);
275 splx(oldspl);
276 }
277 if (errcode != 0 && bootverbose)
278 printf("\tintr_connect(irq%d) failed, result=%d\n",
279 irq, errcode);
280
281 return (errcode);
282}
283
284/*
285 * Remove the interrupt handler descriptor data connected created by an
286 * earlier call of intr_connect() from the linked list and adjust the
287 * interrupt masks if necessary.
288 *
289 * This function deactivates the handler.
290 */
291
292int
293intr_disconnect(intrec *idesc)
294{
295 intrec **hook, *head;
296 int irq;
297 int errcode = 0;
298
299 if (idesc == NULL)
300 return (-1);
301
302 irq = idesc->intr;
303
304 /* find pointer that keeps the reference to this interrupt descriptor */
305 hook = find_pred(idesc, irq);
306 if (hook == NULL)
307 return (-1);
308
309 /* make copy of original list head, the line after may overwrite it */
310 head = intreclist_head[irq];
311
312 /* unlink: make predecessor point to idesc->next instead of to idesc */
313 *hook = idesc->next;
314
315 /* now check whether the element we removed was the list head */
316 if (idesc == head) {
317 intrmask_t oldspl = splq(1 << irq);
318
319 /* we want to remove the list head, which was known to intr_mux */
320 icu_unset(irq, (inthand2_t*)intr_mux);
321
322 /* check whether the new list head is the only element on list */
323 head = intreclist_head[irq];
324 if (head != NULL) {
325 if (head->next != NULL) {
326 /* install the multiplex handler with new list head as argument */
327 errcode = icu_setup(irq, (inthand2_t*)intr_mux, head, 0, 0);
328 if (errcode == 0)
329 update_intrname(irq, -1);
330 } else {
331 /* install the one remaining handler for this irq */
332 errcode = icu_setup(irq, head->handler,
333 head->argument,
334 head->maskptr, head->flags);
335 if (errcode == 0)
336 update_intrname(irq, (intptr_t)head->devdata);
337 }
338 }
339 splx(oldspl);
340 }
341 update_masks(idesc->maskptr, irq);
342#ifdef RESOURCE_CHECK
343 resource_free(idesc->devdata);
344#endif /* RESOURCE_CHECK */
345 return (0);
346}
347
348/*
349 * Create an interrupt handler descriptor data structure, which later can
350 * be activated or deactivated at will by calls of [dis]connect(intrec*).
351 *
352 * The dev_instance pointer is required for resource management, and will
353 * only be passed through to resource_claim().
354 *
355 * The interrupt handler takes an argument of type (void*), which is not
356 * what is currently used for ISA devices. But since the unit number passed
357 * to an ISA interrupt handler can be stored in a (void*) variable, this
358 * causes no problems. Eventually all the ISA interrupt handlers should be
359 * modified to accept the pointer to their private data, too, instead of
360 * an integer index.
361 *
362 * There will be functions that derive a driver and unit name from a
363 * dev_instance variable, and those functions will be used to maintain the
364 * interrupt counter label array referenced by systat and vmstat to report
365 * device interrupt rates (->update_intrlabels).
366 */
367
368intrec *
369intr_create(void *dev_instance, int irq, inthand2_t handler, void *arg,
370 intrmask_t *maskptr, int flags)
371{
372 intrec *idesc;
373
374 if (ICU_LEN > 8 * sizeof *maskptr) {
375 printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n",
376 ICU_LEN, 8 * sizeof *maskptr);
377 return (NULL);
378 }
379 if ((unsigned)irq >= ICU_LEN) {
380 printf("create_intr: requested irq%d too high, limit is %d\n",
381 irq, ICU_LEN -1);
382 return (NULL);
383 }
384
385 idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK);
386 if (idesc) {
387 idesc->next = NULL;
388 bzero(idesc, sizeof *idesc);
389
390 idesc->devdata = dev_instance;
391 idesc->handler = handler;
392 idesc->argument = arg;
393 idesc->maskptr = maskptr;
394 idesc->intr = irq;
395 idesc->flags = flags;
396 }
397 return (idesc);
398}
399
400/*
401 * Return the memory held by the interrupt handler descriptor data structure
402 * to the system. Make sure, the handler is not actively used anymore, before.
403 */
404
405int
406intr_destroy(intrec *rec)
407{
408 if (intr_disconnect(rec) != 0)
409 return (-1);
410 free(rec, M_DEVBUF);
411 return (0);
412}
413
414/*
415 * Emulate the register_intr() call previously defined as low level function.
416 * That function (now icu_setup()) may no longer be directly called, since
417 * a conflict between an ISA and PCI interrupt might go by unnocticed, else.
418 */
419
420int
421register_intr(int intr, int device_id, u_int flags,
422 inthand2_t handler, u_int *maskptr, int unit)
423{
424 /* XXX modify to include isa_device instead of device_id */
425 intrec *idesc;
426
427 flags |= INTR_EXCL;
428 idesc = intr_create((void *)(intptr_t)device_id, intr, handler,
429 (void*)(intptr_t)unit, maskptr, flags);
430 return (intr_connect(idesc));
431}
432
433/*
434 * Emulate the old unregister_intr() low level function.
435 * Make sure there is just one interrupt, that it was
436 * registered as non-shared, and that the handlers match.
437 */
438
439int
440unregister_intr(int intr, inthand2_t handler)
441{
442 intrec *p = intreclist_head[intr];
443
444 if (p != NULL && (p->flags & INTR_EXCL) != 0 && p->handler == handler)
445 return (intr_destroy(p));
446 return (EINVAL);
447}
448
449#endif /* __i386__ */
450
451void
452register_swi(intr, handler)
453 int intr;
454 swihand_t *handler;
455{
456 struct swilist *slp, *slq;
457 int s;
458
459 if (intr < NHWI || intr >= NHWI + NSWI)
460 panic("register_swi: bad intr %d", intr);
461 if (handler == swi_generic || handler == swi_null)
462 panic("register_swi: bad handler %p", (void *)handler);
463 slp = &swilists[intr - NHWI];
464 s = splhigh();
465 if (ihandlers[intr] == swi_null)
466 ihandlers[intr] = handler;
467 else {
468 if (slp->sl_next == NULL) {
469 slp->sl_handler = ihandlers[intr];
470 ihandlers[intr] = swi_generic;
471 }
472 slq = malloc(sizeof(*slq), M_DEVBUF, M_NOWAIT);
473 if (slq == NULL)
474 panic("register_swi: malloc failed");
475 slq->sl_handler = handler;
476 slq->sl_next = NULL;
477 while (slp->sl_next != NULL)
478 slp = slp->sl_next;
479 slp->sl_next = slq;
480 }
481 splx(s);
482}
483
484void
485swi_dispatcher(intr)
486 int intr;
487{
488 struct swilist *slp;
489
490 slp = &swilists[intr - NHWI];
491 do {
492 (*slp->sl_handler)();
493 slp = slp->sl_next;
494 } while (slp != NULL);
495}
496
497void
498unregister_swi(intr, handler)
499 int intr;
500 swihand_t *handler;
501{
502 struct swilist *slfoundpred, *slp, *slq;
503 int s;
504
505 if (intr < NHWI || intr >= NHWI + NSWI)
506 panic("unregister_swi: bad intr %d", intr);
507 if (handler == swi_generic || handler == swi_null)
508 panic("unregister_swi: bad handler %p", (void *)handler);
509 slp = &swilists[intr - NHWI];
510 s = splhigh();
511 if (ihandlers[intr] == handler)
512 ihandlers[intr] = swi_null;
513 else if (slp->sl_next != NULL) {
514 slfoundpred = NULL;
515 for (slq = slp->sl_next; slq != NULL;
516 slp = slq, slq = slp->sl_next)
517 if (slq->sl_handler == handler)
518 slfoundpred = slp;
519 slp = &swilists[intr - NHWI];
520 if (slfoundpred != NULL) {
521 slq = slfoundpred->sl_next;
522 slfoundpred->sl_next = slq->sl_next;
523 free(slq, M_DEVBUF);
524 } else if (slp->sl_handler == handler) {
525 slq = slp->sl_next;
526 slp->sl_next = slq->sl_next;
527 slp->sl_handler = slq->sl_handler;
528 free(slq, M_DEVBUF);
529 }
530 if (slp->sl_next == NULL)
531 ihandlers[intr] = slp->sl_handler;
532 }
533 splx(s);
534}
535
34#include <sys/errno.h>
35#ifdef RESOURCE_CHECK
36#include <sys/drvresource.h>
37#endif /* RESOURCE_CHECK */
38
39#include <machine/ipl.h>
40
41#ifdef __i386__
42#include <i386/isa/icu.h>
43#include <i386/isa/intr_machdep.h>
44#endif
45
46#include <sys/interrupt.h>
47
48#include <stddef.h>
49
50#ifdef __i386__
51
52typedef struct intrec {
53 intrmask_t mask;
54 inthand2_t *handler;
55 void *argument;
56 struct intrec *next;
57 void *devdata;
58 int intr;
59 intrmask_t *maskptr;
60 int flags;
61} intrec;
62
63static intrec *intreclist_head[NHWI];
64
65#endif
66
67struct swilist {
68 swihand_t *sl_handler;
69 struct swilist *sl_next;
70};
71
72static struct swilist swilists[NSWI];
73
74#ifdef __i386__
75
76/*
77 * The interrupt multiplexer calls each of the handlers in turn,
78 * and applies the associated interrupt mask to "cpl", which is
79 * defined as a ".long" in /sys/i386/isa/ipl.s
80 */
81
82#ifndef SMP
83static __inline intrmask_t
84splq(intrmask_t mask)
85{
86 intrmask_t tmp = cpl;
87 cpl |= mask;
88 return (tmp);
89}
90#endif /* SMP */
91
92static void
93intr_mux(void *arg)
94{
95 intrec *p = arg;
96
97 while (p != NULL) {
98 int oldspl = splq(p->mask);
99 p->handler(p->argument);
100 splx(oldspl);
101 p = p->next;
102 }
103}
104
105static intrec*
106find_idesc(unsigned *maskptr, int irq)
107{
108 intrec *p = intreclist_head[irq];
109
110 while (p && p->maskptr != maskptr)
111 p = p->next;
112
113 return (p);
114}
115
116static intrec**
117find_pred(intrec *idesc, int irq)
118{
119 intrec **pp = &intreclist_head[irq];
120 intrec *p = *pp;
121
122 while (p != idesc) {
123 if (p == NULL)
124 return (NULL);
125 pp = &p->next;
126 p = *pp;
127 }
128 return (pp);
129}
130
131/*
132 * Both the low level handler and the shared interrupt multiplexer
133 * block out further interrupts as set in the handlers "mask", while
134 * the handler is running. In fact *maskptr should be used for this
135 * purpose, but since this requires one more pointer dereference on
136 * each interrupt, we rather bother update "mask" whenever *maskptr
137 * changes. The function "update_masks" should be called **after**
138 * all manipulation of the linked list of interrupt handlers hung
139 * off of intrdec_head[irq] is complete, since the chain of handlers
140 * will both determine the *maskptr values and the instances of mask
141 * that are fixed. This function should be called with the irq for
142 * which a new handler has been add blocked, since the masks may not
143 * yet know about the use of this irq for a device of a certain class.
144 */
145
146static void
147update_mux_masks(void)
148{
149 int irq;
150 for (irq = 0; irq < ICU_LEN; irq++) {
151 intrec *idesc = intreclist_head[irq];
152 while (idesc != NULL) {
153 if (idesc->maskptr != NULL) {
154 /* our copy of *maskptr may be stale, refresh */
155 idesc->mask = *idesc->maskptr;
156 }
157 idesc = idesc->next;
158 }
159 }
160}
161
162static void
163update_masks(intrmask_t *maskptr, int irq)
164{
165 intrmask_t mask = 1 << irq;
166
167 if (maskptr == NULL)
168 return;
169
170 if (find_idesc(maskptr, irq) == NULL) {
171 /* no reference to this maskptr was found in this irq's chain */
172 if ((*maskptr & mask) == 0)
173 return;
174 /* the irq was included in the classes mask, remove it */
175 INTRUNMASK(*maskptr, mask);
176 } else {
177 /* a reference to this maskptr was found in this irq's chain */
178 if ((*maskptr & mask) != 0)
179 return;
180 /* put the irq into the classes mask */
181 INTRMASK(*maskptr, mask);
182 }
183 /* we need to update all values in the intr_mask[irq] array */
184 update_intr_masks();
185 /* update mask in chains of the interrupt multiplex handler as well */
186 update_mux_masks();
187}
188
189/*
190 * Add interrupt handler to linked list hung off of intreclist_head[irq]
191 * and install shared interrupt multiplex handler, if necessary
192 */
193
194static int
195add_intrdesc(intrec *idesc)
196{
197 int irq = idesc->intr;
198
199 intrec *head = intreclist_head[irq];
200
201 if (head == NULL) {
202 /* first handler for this irq, just install it */
203 if (icu_setup(irq, idesc->handler, idesc->argument,
204 idesc->maskptr, idesc->flags) != 0)
205 return (-1);
206
207 update_intrname(irq, (intptr_t)idesc->devdata);
208 /* keep reference */
209 intreclist_head[irq] = idesc;
210 } else {
211 if ((idesc->flags & INTR_EXCL) != 0
212 || (head->flags & INTR_EXCL) != 0) {
213 /*
214 * can't append new handler, if either list head or
215 * new handler do not allow interrupts to be shared
216 */
217 if (bootverbose)
218 printf("\tdevice combination doesn't support "
219 "shared irq%d\n", irq);
220 return (-1);
221 }
222 if (head->next == NULL) {
223 /*
224 * second handler for this irq, replace device driver's
225 * handler by shared interrupt multiplexer function
226 */
227 icu_unset(irq, head->handler);
228 if (icu_setup(irq, (inthand2_t*)intr_mux, head, 0, 0) != 0)
229 return (-1);
230 if (bootverbose)
231 printf("\tusing shared irq%d.\n", irq);
232 update_intrname(irq, -1);
233 }
234 /* just append to the end of the chain */
235 while (head->next != NULL)
236 head = head->next;
237 head->next = idesc;
238 }
239 update_masks(idesc->maskptr, irq);
240 return (0);
241}
242
243/*
244 * Add the interrupt handler descriptor data structure created by an
245 * earlier call of create_intr() to the linked list for its irq and
246 * adjust the interrupt masks if necessary.
247 *
248 * This function effectively activates the handler.
249 */
250
251int
252intr_connect(intrec *idesc)
253{
254 int errcode = -1;
255 int irq;
256
257#ifdef RESOURCE_CHECK
258 int resflag;
259#endif /* RESOURCE_CHECK */
260
261 if (idesc == NULL)
262 return (-1);
263
264 irq = idesc->intr;
265#ifdef RESOURCE_CHECK
266 resflag = (idesc->flags & INTR_EXCL) ? RESF_NONE : RESF_SHARED;
267 if (resource_claim(idesc->devdata, REST_INT, resflag, irq, irq) == 0)
268#endif /* RESOURCE_CHECK */
269 {
270 /* block this irq */
271 intrmask_t oldspl = splq(1 << irq);
272
273 /* add irq to class selected by maskptr */
274 errcode = add_intrdesc(idesc);
275 splx(oldspl);
276 }
277 if (errcode != 0 && bootverbose)
278 printf("\tintr_connect(irq%d) failed, result=%d\n",
279 irq, errcode);
280
281 return (errcode);
282}
283
284/*
285 * Remove the interrupt handler descriptor data connected created by an
286 * earlier call of intr_connect() from the linked list and adjust the
287 * interrupt masks if necessary.
288 *
289 * This function deactivates the handler.
290 */
291
292int
293intr_disconnect(intrec *idesc)
294{
295 intrec **hook, *head;
296 int irq;
297 int errcode = 0;
298
299 if (idesc == NULL)
300 return (-1);
301
302 irq = idesc->intr;
303
304 /* find pointer that keeps the reference to this interrupt descriptor */
305 hook = find_pred(idesc, irq);
306 if (hook == NULL)
307 return (-1);
308
309 /* make copy of original list head, the line after may overwrite it */
310 head = intreclist_head[irq];
311
312 /* unlink: make predecessor point to idesc->next instead of to idesc */
313 *hook = idesc->next;
314
315 /* now check whether the element we removed was the list head */
316 if (idesc == head) {
317 intrmask_t oldspl = splq(1 << irq);
318
319 /* we want to remove the list head, which was known to intr_mux */
320 icu_unset(irq, (inthand2_t*)intr_mux);
321
322 /* check whether the new list head is the only element on list */
323 head = intreclist_head[irq];
324 if (head != NULL) {
325 if (head->next != NULL) {
326 /* install the multiplex handler with new list head as argument */
327 errcode = icu_setup(irq, (inthand2_t*)intr_mux, head, 0, 0);
328 if (errcode == 0)
329 update_intrname(irq, -1);
330 } else {
331 /* install the one remaining handler for this irq */
332 errcode = icu_setup(irq, head->handler,
333 head->argument,
334 head->maskptr, head->flags);
335 if (errcode == 0)
336 update_intrname(irq, (intptr_t)head->devdata);
337 }
338 }
339 splx(oldspl);
340 }
341 update_masks(idesc->maskptr, irq);
342#ifdef RESOURCE_CHECK
343 resource_free(idesc->devdata);
344#endif /* RESOURCE_CHECK */
345 return (0);
346}
347
348/*
349 * Create an interrupt handler descriptor data structure, which later can
350 * be activated or deactivated at will by calls of [dis]connect(intrec*).
351 *
352 * The dev_instance pointer is required for resource management, and will
353 * only be passed through to resource_claim().
354 *
355 * The interrupt handler takes an argument of type (void*), which is not
356 * what is currently used for ISA devices. But since the unit number passed
357 * to an ISA interrupt handler can be stored in a (void*) variable, this
358 * causes no problems. Eventually all the ISA interrupt handlers should be
359 * modified to accept the pointer to their private data, too, instead of
360 * an integer index.
361 *
362 * There will be functions that derive a driver and unit name from a
363 * dev_instance variable, and those functions will be used to maintain the
364 * interrupt counter label array referenced by systat and vmstat to report
365 * device interrupt rates (->update_intrlabels).
366 */
367
368intrec *
369intr_create(void *dev_instance, int irq, inthand2_t handler, void *arg,
370 intrmask_t *maskptr, int flags)
371{
372 intrec *idesc;
373
374 if (ICU_LEN > 8 * sizeof *maskptr) {
375 printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n",
376 ICU_LEN, 8 * sizeof *maskptr);
377 return (NULL);
378 }
379 if ((unsigned)irq >= ICU_LEN) {
380 printf("create_intr: requested irq%d too high, limit is %d\n",
381 irq, ICU_LEN -1);
382 return (NULL);
383 }
384
385 idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK);
386 if (idesc) {
387 idesc->next = NULL;
388 bzero(idesc, sizeof *idesc);
389
390 idesc->devdata = dev_instance;
391 idesc->handler = handler;
392 idesc->argument = arg;
393 idesc->maskptr = maskptr;
394 idesc->intr = irq;
395 idesc->flags = flags;
396 }
397 return (idesc);
398}
399
400/*
401 * Return the memory held by the interrupt handler descriptor data structure
402 * to the system. Make sure, the handler is not actively used anymore, before.
403 */
404
405int
406intr_destroy(intrec *rec)
407{
408 if (intr_disconnect(rec) != 0)
409 return (-1);
410 free(rec, M_DEVBUF);
411 return (0);
412}
413
414/*
415 * Emulate the register_intr() call previously defined as low level function.
416 * That function (now icu_setup()) may no longer be directly called, since
417 * a conflict between an ISA and PCI interrupt might go by unnocticed, else.
418 */
419
420int
421register_intr(int intr, int device_id, u_int flags,
422 inthand2_t handler, u_int *maskptr, int unit)
423{
424 /* XXX modify to include isa_device instead of device_id */
425 intrec *idesc;
426
427 flags |= INTR_EXCL;
428 idesc = intr_create((void *)(intptr_t)device_id, intr, handler,
429 (void*)(intptr_t)unit, maskptr, flags);
430 return (intr_connect(idesc));
431}
432
433/*
434 * Emulate the old unregister_intr() low level function.
435 * Make sure there is just one interrupt, that it was
436 * registered as non-shared, and that the handlers match.
437 */
438
439int
440unregister_intr(int intr, inthand2_t handler)
441{
442 intrec *p = intreclist_head[intr];
443
444 if (p != NULL && (p->flags & INTR_EXCL) != 0 && p->handler == handler)
445 return (intr_destroy(p));
446 return (EINVAL);
447}
448
449#endif /* __i386__ */
450
451void
452register_swi(intr, handler)
453 int intr;
454 swihand_t *handler;
455{
456 struct swilist *slp, *slq;
457 int s;
458
459 if (intr < NHWI || intr >= NHWI + NSWI)
460 panic("register_swi: bad intr %d", intr);
461 if (handler == swi_generic || handler == swi_null)
462 panic("register_swi: bad handler %p", (void *)handler);
463 slp = &swilists[intr - NHWI];
464 s = splhigh();
465 if (ihandlers[intr] == swi_null)
466 ihandlers[intr] = handler;
467 else {
468 if (slp->sl_next == NULL) {
469 slp->sl_handler = ihandlers[intr];
470 ihandlers[intr] = swi_generic;
471 }
472 slq = malloc(sizeof(*slq), M_DEVBUF, M_NOWAIT);
473 if (slq == NULL)
474 panic("register_swi: malloc failed");
475 slq->sl_handler = handler;
476 slq->sl_next = NULL;
477 while (slp->sl_next != NULL)
478 slp = slp->sl_next;
479 slp->sl_next = slq;
480 }
481 splx(s);
482}
483
484void
485swi_dispatcher(intr)
486 int intr;
487{
488 struct swilist *slp;
489
490 slp = &swilists[intr - NHWI];
491 do {
492 (*slp->sl_handler)();
493 slp = slp->sl_next;
494 } while (slp != NULL);
495}
496
497void
498unregister_swi(intr, handler)
499 int intr;
500 swihand_t *handler;
501{
502 struct swilist *slfoundpred, *slp, *slq;
503 int s;
504
505 if (intr < NHWI || intr >= NHWI + NSWI)
506 panic("unregister_swi: bad intr %d", intr);
507 if (handler == swi_generic || handler == swi_null)
508 panic("unregister_swi: bad handler %p", (void *)handler);
509 slp = &swilists[intr - NHWI];
510 s = splhigh();
511 if (ihandlers[intr] == handler)
512 ihandlers[intr] = swi_null;
513 else if (slp->sl_next != NULL) {
514 slfoundpred = NULL;
515 for (slq = slp->sl_next; slq != NULL;
516 slp = slq, slq = slp->sl_next)
517 if (slq->sl_handler == handler)
518 slfoundpred = slp;
519 slp = &swilists[intr - NHWI];
520 if (slfoundpred != NULL) {
521 slq = slfoundpred->sl_next;
522 slfoundpred->sl_next = slq->sl_next;
523 free(slq, M_DEVBUF);
524 } else if (slp->sl_handler == handler) {
525 slq = slp->sl_next;
526 slp->sl_next = slq->sl_next;
527 slp->sl_handler = slq->sl_handler;
528 free(slq, M_DEVBUF);
529 }
530 if (slp->sl_next == NULL)
531 ihandlers[intr] = slp->sl_handler;
532 }
533 splx(s);
534}
535