source: mainline/kernel/generic/src/ipc/irq.c@ 11675207

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 11675207 was 11675207, checked in by jermar <jermar@…>, 17 years ago

Move everything to kernel/.

  • Property mode set to 100644
File size: 8.2 KB
Line 
1/*
2 * Copyright (C) 2006 Ondrej Palkovsky
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 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - 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 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup genericipc
30 * @{
31 */
32/**
33 * @file
34 * @brief IRQ notification framework.
35 *
36 * This framework allows applications to register to receive a notification
37 * when interrupt is detected. The application may provide a simple 'top-half'
38 * handler as part of its registration, which can perform simple operations
39 * (read/write port/memory, add information to notification ipc message).
40 *
41 * The structure of a notification message is as follows:
42 * - METHOD: interrupt number
43 * - ARG1: payload modified by a 'top-half' handler
44 * - ARG2: payload
45 * - ARG3: payload
46 * - in_phone_hash: interrupt counter (may be needed to assure correct order
47 * in multithreaded drivers)
48 */
49
50#include <arch.h>
51#include <mm/slab.h>
52#include <errno.h>
53#include <ipc/ipc.h>
54#include <ipc/irq.h>
55#include <atomic.h>
56#include <syscall/copy.h>
57#include <console/console.h>
58
59typedef struct {
60 SPINLOCK_DECLARE(lock);
61 answerbox_t *box;
62 irq_code_t *code;
63 atomic_t counter;
64} ipc_irq_t;
65
66
67static ipc_irq_t *irq_conns = NULL;
68static int irq_conns_size;
69
70#include <print.h>
71/* Execute code associated with IRQ notification */
72static void code_execute(call_t *call, irq_code_t *code)
73{
74 int i;
75 unative_t dstval = 0;
76
77 if (!code)
78 return;
79
80 for (i=0; i < code->cmdcount;i++) {
81 switch (code->cmds[i].cmd) {
82 case CMD_MEM_READ_1:
83 dstval = *((uint8_t *)code->cmds[i].addr);
84 break;
85 case CMD_MEM_READ_2:
86 dstval = *((uint16_t *)code->cmds[i].addr);
87 break;
88 case CMD_MEM_READ_4:
89 dstval = *((uint32_t *)code->cmds[i].addr);
90 break;
91 case CMD_MEM_READ_8:
92 dstval = *((uint64_t *)code->cmds[i].addr);
93 break;
94 case CMD_MEM_WRITE_1:
95 *((uint8_t *)code->cmds[i].addr) = code->cmds[i].value;
96 break;
97 case CMD_MEM_WRITE_2:
98 *((uint16_t *)code->cmds[i].addr) = code->cmds[i].value;
99 break;
100 case CMD_MEM_WRITE_4:
101 *((uint32_t *)code->cmds[i].addr) = code->cmds[i].value;
102 break;
103 case CMD_MEM_WRITE_8:
104 *((uint64_t *)code->cmds[i].addr) = code->cmds[i].value;
105 break;
106#if defined(ia32) || defined(amd64)
107 case CMD_PORT_READ_1:
108 dstval = inb((long)code->cmds[i].addr);
109 break;
110 case CMD_PORT_WRITE_1:
111 outb((long)code->cmds[i].addr, code->cmds[i].value);
112 break;
113#endif
114#if defined(ia64)
115 case CMD_IA64_GETCHAR:
116 dstval = _getc(&ski_uconsole);
117 break;
118#endif
119#if defined(ppc32)
120 case CMD_PPC32_GETCHAR:
121 dstval = cuda_get_scancode();
122 break;
123#endif
124 default:
125 break;
126 }
127 if (code->cmds[i].dstarg && code->cmds[i].dstarg < 4) {
128 call->data.args[code->cmds[i].dstarg] = dstval;
129 }
130 }
131}
132
133static void code_free(irq_code_t *code)
134{
135 if (code) {
136 free(code->cmds);
137 free(code);
138 }
139}
140
141static irq_code_t * code_from_uspace(irq_code_t *ucode)
142{
143 irq_code_t *code;
144 irq_cmd_t *ucmds;
145 int rc;
146
147 code = malloc(sizeof(*code), 0);
148 rc = copy_from_uspace(code, ucode, sizeof(*code));
149 if (rc != 0) {
150 free(code);
151 return NULL;
152 }
153
154 if (code->cmdcount > IRQ_MAX_PROG_SIZE) {
155 free(code);
156 return NULL;
157 }
158 ucmds = code->cmds;
159 code->cmds = malloc(sizeof(code->cmds[0]) * (code->cmdcount), 0);
160 rc = copy_from_uspace(code->cmds, ucmds, sizeof(code->cmds[0]) * (code->cmdcount));
161 if (rc != 0) {
162 free(code->cmds);
163 free(code);
164 return NULL;
165 }
166
167 return code;
168}
169
170/** Unregister task from irq */
171void ipc_irq_unregister(answerbox_t *box, int irq)
172{
173 ipl_t ipl;
174 int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
175
176 ipl = interrupts_disable();
177 spinlock_lock(&irq_conns[mq].lock);
178 if (irq_conns[mq].box == box) {
179 irq_conns[mq].box = NULL;
180 code_free(irq_conns[mq].code);
181 irq_conns[mq].code = NULL;
182 }
183
184 spinlock_unlock(&irq_conns[mq].lock);
185 interrupts_restore(ipl);
186}
187
188/** Register an answerbox as a receiving end of interrupts notifications */
189int ipc_irq_register(answerbox_t *box, int irq, irq_code_t *ucode)
190{
191 ipl_t ipl;
192 irq_code_t *code;
193 int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
194
195 ASSERT(irq_conns);
196
197 if (ucode) {
198 code = code_from_uspace(ucode);
199 if (!code)
200 return EBADMEM;
201 } else
202 code = NULL;
203
204 ipl = interrupts_disable();
205 spinlock_lock(&irq_conns[mq].lock);
206
207 if (irq_conns[mq].box) {
208 spinlock_unlock(&irq_conns[mq].lock);
209 interrupts_restore(ipl);
210 code_free(code);
211 return EEXISTS;
212 }
213 irq_conns[mq].box = box;
214 irq_conns[mq].code = code;
215 atomic_set(&irq_conns[mq].counter, 0);
216 spinlock_unlock(&irq_conns[mq].lock);
217 interrupts_restore(ipl);
218
219 return 0;
220}
221
222/** Add call to proper answerbox queue
223 *
224 * Assume irq_conns[mq].lock is locked */
225static void send_call(int mq, call_t *call)
226{
227 spinlock_lock(&irq_conns[mq].box->irq_lock);
228 list_append(&call->link, &irq_conns[mq].box->irq_notifs);
229 spinlock_unlock(&irq_conns[mq].box->irq_lock);
230
231 waitq_wakeup(&irq_conns[mq].box->wq, 0);
232}
233
234/** Send notification message
235 *
236 */
237void ipc_irq_send_msg(int irq, unative_t a1, unative_t a2, unative_t a3)
238{
239 call_t *call;
240 int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
241
242 spinlock_lock(&irq_conns[mq].lock);
243
244 if (irq_conns[mq].box) {
245 call = ipc_call_alloc(FRAME_ATOMIC);
246 if (!call) {
247 spinlock_unlock(&irq_conns[mq].lock);
248 return;
249 }
250 call->flags |= IPC_CALL_NOTIF;
251 IPC_SET_METHOD(call->data, irq);
252 IPC_SET_ARG1(call->data, a1);
253 IPC_SET_ARG2(call->data, a2);
254 IPC_SET_ARG3(call->data, a3);
255 /* Put a counter to the message */
256 call->private = atomic_preinc(&irq_conns[mq].counter);
257
258 send_call(mq, call);
259 }
260 spinlock_unlock(&irq_conns[mq].lock);
261}
262
263/** Notify task that an irq had occurred.
264 *
265 * We expect interrupts to be disabled
266 */
267void ipc_irq_send_notif(int irq)
268{
269 call_t *call;
270 int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
271
272 ASSERT(irq_conns);
273 spinlock_lock(&irq_conns[mq].lock);
274
275 if (irq_conns[mq].box) {
276 call = ipc_call_alloc(FRAME_ATOMIC);
277 if (!call) {
278 spinlock_unlock(&irq_conns[mq].lock);
279 return;
280 }
281 call->flags |= IPC_CALL_NOTIF;
282 /* Put a counter to the message */
283 call->private = atomic_preinc(&irq_conns[mq].counter);
284 /* Set up args */
285 IPC_SET_METHOD(call->data, irq);
286
287 /* Execute code to handle irq */
288 code_execute(call, irq_conns[mq].code);
289
290 send_call(mq, call);
291 }
292
293 spinlock_unlock(&irq_conns[mq].lock);
294}
295
296
297/** Initialize table of interrupt handlers
298 *
299 * @param irqcount Count of required hardware IRQs to be supported
300 */
301void ipc_irq_make_table(int irqcount)
302{
303 int i;
304
305 irqcount += IPC_IRQ_RESERVED_VIRTUAL;
306
307 irq_conns_size = irqcount;
308 irq_conns = malloc(irqcount * (sizeof(*irq_conns)), 0);
309 for (i=0; i < irqcount; i++) {
310 spinlock_initialize(&irq_conns[i].lock, "irq_ipc_lock");
311 irq_conns[i].box = NULL;
312 irq_conns[i].code = NULL;
313 }
314}
315
316/** Disconnect all irq's notifications
317 *
318 * @todo It may be better to do some linked list, so that
319 * we wouldn't need to go through whole array every cleanup
320 */
321void ipc_irq_cleanup(answerbox_t *box)
322{
323 int i;
324 ipl_t ipl;
325
326 for (i=0; i < irq_conns_size; i++) {
327 ipl = interrupts_disable();
328 spinlock_lock(&irq_conns[i].lock);
329 if (irq_conns[i].box == box)
330 irq_conns[i].box = NULL;
331 spinlock_unlock(&irq_conns[i].lock);
332 interrupts_restore(ipl);
333 }
334}
335
336/** @}
337 */
Note: See TracBrowser for help on using the repository browser.