irq: add avr_unconnect_irq() and avr_irq_unregister_notify()
[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 static void
38 _avr_irq_pool_add(
39                 avr_irq_pool_t * pool,
40                 avr_irq_t * irq)
41 {
42         if ((pool->count & 0xf) == 0) {
43                 pool->irq = (avr_irq_t**)realloc(pool->irq,
44                                 (pool->count + 16) * sizeof(avr_irq_t *));
45         }
46         pool->irq[pool->count++] = irq;
47         irq->pool = pool;
48 }
49
50 static void
51 _avr_irq_pool_remove(
52                 avr_irq_pool_t * pool,
53                 avr_irq_t * irq)
54 {
55         for (int i = 0; i < pool->count; i++)
56                 if (pool->irq[i] == irq) {
57                         pool->irq[i] = 0;
58                         return;
59                 }
60 }
61
62 void
63 avr_init_irq(
64                 avr_irq_pool_t * pool,
65                 avr_irq_t * irq,
66                 uint32_t base,
67                 uint32_t count,
68                 const char ** names /* optional */)
69 {
70         memset(irq, 0, sizeof(avr_irq_t) * count);
71
72         for (int i = 0; i < count; i++) {
73                 irq[i].irq = base + i;
74                 if (pool)
75                         _avr_irq_pool_add(pool, &irq[i]);
76                 if (names && names[i])
77                         irq[i].name = strdup(names[i]);
78         }
79 }
80
81 avr_irq_t *
82 avr_alloc_irq(
83                 avr_irq_pool_t * pool,
84                 uint32_t base,
85                 uint32_t count,
86                 const char ** names /* optional */)
87 {
88         avr_irq_t * irq = (avr_irq_t*)malloc(sizeof(avr_irq_t) * count);
89         avr_init_irq(pool, irq, base, count, names);
90         for (int i = 0; i < count; i++)
91                 irq[i].flags |= IRQ_FLAG_ALLOC; 
92         return irq;
93 }
94
95 static avr_irq_hook_t *
96 _avr_alloc_irq_hook(
97                 avr_irq_t * irq)
98 {
99         avr_irq_hook_t *hook = malloc(sizeof(avr_irq_hook_t));
100         memset(hook, 0, sizeof(avr_irq_hook_t));
101         hook->next = irq->hook;
102         irq->hook = hook;
103         return hook;
104 }
105
106 void
107 avr_free_irq(
108                 avr_irq_t * irq,
109                 uint32_t count)
110 {
111         if (!irq || !count)
112                 return;
113         for (int i = 0; i < count; i++) {
114                 avr_irq_t * iq = irq + i;
115                 if (iq->pool)
116                         _avr_irq_pool_remove(iq->pool, iq);
117                 if (iq->name)
118                         free((char*)iq->name);
119                 iq->name = NULL;
120                 // purge hooks
121                 avr_irq_hook_t *hook = iq->hook;
122                 while (hook) {
123                         avr_irq_hook_t * next = hook->next;
124                         free(hook);
125                         hook = next;
126                 }
127                 iq->hook = NULL;
128         }
129         // if that irq list was allocated by us, free it
130         if (irq->flags & IRQ_FLAG_ALLOC)
131                 free(irq);
132 }
133
134 void
135 avr_irq_register_notify(
136                 avr_irq_t * irq,
137                 avr_irq_notify_t notify,
138                 void * param)
139 {
140         if (!irq || !notify)
141                 return;
142         
143         avr_irq_hook_t *hook = irq->hook;
144         while (hook) {
145                 if (hook->notify == notify && hook->param == param)
146                         return; // already there
147                 hook = hook->next;
148         }
149         hook = _avr_alloc_irq_hook(irq);
150         hook->notify = notify;
151         hook->param = param;
152 }
153
154 void
155 avr_irq_unregister_notify(
156                 avr_irq_t * irq,
157                 avr_irq_notify_t notify,
158                 void * param)
159 {
160         avr_irq_hook_t *hook, *prev;
161         if (!irq || !notify)
162                 return;
163
164         hook = irq->hook;
165         prev = NULL;
166         while (hook) {
167                 if (hook->notify == notify && hook->param == param)
168                 {
169                         if ( prev )
170                                 prev->next = hook->next;
171                         else
172                                 irq->hook = hook->next;
173                         free(hook);
174                         return;
175                 }
176                 prev = hook;
177                 hook = hook->next;
178         }
179 }
180
181 void
182 avr_raise_irq(
183                 avr_irq_t * irq,
184                 uint32_t value)
185 {
186         if (!irq)
187                 return ;
188         uint32_t output = (irq->flags & IRQ_FLAG_NOT) ? !value : value;
189         if (irq->value == output && (irq->flags & IRQ_FLAG_FILTERED))
190                 return;
191         avr_irq_hook_t *hook = irq->hook;
192         while (hook) {
193                 avr_irq_hook_t * next = hook->next;
194                         // prevents reentrance / endless calling loops
195                 if (hook->busy == 0) {
196                         hook->busy++;
197                         if (hook->notify)
198                                 hook->notify(irq, output,  hook->param);
199                         if (hook->chain)
200                                 avr_raise_irq(hook->chain, output);
201                         hook->busy--;
202                 }                       
203                 hook = next;
204         }
205         // the value is set after the callbacks are called, so the callbacks
206         // can themselves compare for old/new values between their parameter
207         // they are passed (new value) and the previous irq->value
208         irq->value = output;
209 }
210
211 void
212 avr_connect_irq(
213                 avr_irq_t * src,
214                 avr_irq_t * dst)
215 {
216         if (!src || !dst || src == dst) {
217                 printf("avr_connect_irq invalid irq %p/%p", src, dst);
218                 return;
219         }
220         avr_irq_hook_t *hook = src->hook;
221         while (hook) {
222                 if (hook->chain == dst)
223                         return; // already there
224                 hook = hook->next;
225         }
226         hook = _avr_alloc_irq_hook(src);
227         hook->chain = dst;
228 }
229
230 void
231 avr_unconnect_irq(
232                 avr_irq_t * src,
233                 avr_irq_t * dst)
234 {
235         avr_irq_hook_t *hook, *prev;
236
237         if (!src || !dst || src == dst) {
238                 printf("error: avr_connect_irq invalid irq %p/%p", src, dst); fflush(stdout);
239                 return;
240         }
241         hook = src->hook;
242         prev = NULL;
243         while (hook) {
244                 if (hook->chain == dst)
245                 {
246                         if ( prev )
247                                 prev->next = hook->next;
248                         else
249                                 src->hook = hook->next;
250                         free(hook);
251                         return;
252                 }
253                 prev = hook;
254                 hook = hook->next;
255         }
256 }