irq: Introduce a "irq pool"
[simavr] / simavr / sim / sim_irq.c
1 /*
2         sim_irq.c
3
4         Copyright 2008, 2009 Michel Pollet <buserror@gmail.com>
5
6         This file is part of simavr.
7
8         simavr is free software: you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 3 of the License, or
11         (at your option) any later version.
12
13         simavr is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License
19         along with simavr.  If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include "sim_irq.h"
26
27 // internal structure for a hook, never seen by the notify procs
28 typedef struct avr_irq_hook_t {
29         struct avr_irq_hook_t * next;
30         int busy;       // prevent reentrance of callbacks
31         
32         struct avr_irq_t * chain;       // raise the IRQ on this too - optional if "notify" is on
33         avr_irq_notify_t notify;        // called when IRQ is raised - optional if "chain" is on
34         void * param;                           // "notify" parameter
35 } avr_irq_hook_t;
36
37 void
38 avr_init_irq(
39                 avr_irq_pool_t * pool,
40                 avr_irq_t * irq,
41                 uint32_t base,
42                 uint32_t count,
43                 const char ** names /* optional */)
44 {
45         memset(irq, 0, sizeof(avr_irq_t) * count);
46
47         for (int i = 0; i < count; i++) {
48                 irq[i].irq = base + i;
49         }
50 }
51
52 avr_irq_t *
53 avr_alloc_irq(
54                 avr_irq_pool_t * pool,
55                 uint32_t base,
56                 uint32_t count,
57                 const char ** names /* optional */)
58 {
59         avr_irq_t * irq = (avr_irq_t*)malloc(sizeof(avr_irq_t) * count);
60         avr_init_irq(pool, irq, base, count, names);
61         for (int i = 0; i < count; i++)
62                 irq[i].flags |= IRQ_FLAG_ALLOC; 
63         return irq;
64 }
65
66 static avr_irq_hook_t *
67 _avr_alloc_irq_hook(
68                 avr_irq_t * irq)
69 {
70         avr_irq_hook_t *hook = malloc(sizeof(avr_irq_hook_t));
71         memset(hook, 0, sizeof(avr_irq_hook_t));
72         hook->next = irq->hook;
73         irq->hook = hook;
74         return hook;
75 }
76
77 void
78 avr_free_irq(
79                 avr_irq_t * irq,
80                 uint32_t count)
81 {
82         if (!irq || !count)
83                 return;
84         for (int i = 0; i < count; i++) {
85                 // purge hooks
86                 avr_irq_hook_t *hook = irq->hook;
87                 while (hook) {
88                         avr_irq_hook_t * next = hook->next;
89                         free(hook);
90                         hook = next;
91                 }
92                 irq->hook = NULL;
93         }
94         // if that irq list was allocated by us, free it
95         if (irq->flags & IRQ_FLAG_ALLOC)
96                 free(irq);
97 }
98
99 void
100 avr_irq_register_notify(
101                 avr_irq_t * irq,
102                 avr_irq_notify_t notify,
103                 void * param)
104 {
105         if (!irq || !notify)
106                 return;
107         
108         avr_irq_hook_t *hook = irq->hook;
109         while (hook) {
110                 if (hook->notify == notify && hook->param == param)
111                         return; // already there
112                 hook = hook->next;
113         }
114         hook = _avr_alloc_irq_hook(irq);
115         hook->notify = notify;
116         hook->param = param;
117 }
118
119 void
120 avr_raise_irq(
121                 avr_irq_t * irq,
122                 uint32_t value)
123 {
124         if (!irq)
125                 return ;
126         uint32_t output = (irq->flags & IRQ_FLAG_NOT) ? !value : value;
127         if (irq->value == output && (irq->flags & IRQ_FLAG_FILTERED))
128                 return;
129         avr_irq_hook_t *hook = irq->hook;
130         while (hook) {
131                 avr_irq_hook_t * next = hook->next;
132                         // prevents reentrance / endless calling loops
133                 if (hook->busy == 0) {
134                         hook->busy++;
135                         if (hook->notify)
136                                 hook->notify(irq, output,  hook->param);
137                         if (hook->chain)
138                                 avr_raise_irq(hook->chain, output);
139                         hook->busy--;
140                 }                       
141                 hook = next;
142         }
143         // the value is set after the callbacks are called, so the callbacks
144         // can themselves compare for old/new values between their parameter
145         // they are passed (new value) and the previous irq->value
146         irq->value = output;
147 }
148
149 void
150 avr_connect_irq(
151                 avr_irq_t * src,
152                 avr_irq_t * dst)
153 {
154         if (!src || !dst || src == dst) {
155                 printf("avr_connect_irq invalid irq %p/%p", src, dst);
156                 return;
157         }
158         avr_irq_hook_t *hook = src->hook;
159         while (hook) {
160                 if (hook->chain == dst)
161                         return; // already there
162                 hook = hook->next;
163         }
164         hook = _avr_alloc_irq_hook(src);
165         hook->chain = dst;
166 }