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