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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6eabb6e6 was 253f35a1, checked in by Jakub Jermar <jakub@…>, 19 years ago

sparc64 work.

  • Changes to enable userspace keyboard drivers.
  • Fix z8530 initialization (i.e. clear any pending Tx interrupts).
  • Experimental support for framebuffers with inverted colors.
  • 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#include <print.h>
59
60typedef struct {
61 SPINLOCK_DECLARE(lock);
62 answerbox_t *box;
63 irq_code_t *code;
64 atomic_t counter;
65} ipc_irq_t;
66
67
68static ipc_irq_t *irq_conns = NULL;
69static int irq_conns_size;
70
71
72/* Execute code associated with IRQ notification */
73static void code_execute(call_t *call, irq_code_t *code)
74{
75 int i;
76 unative_t dstval = 0;
77
78 if (!code)
79 return;
80
81 for (i=0; i < code->cmdcount;i++) {
82 switch (code->cmds[i].cmd) {
83 case CMD_MEM_READ_1:
84 dstval = *((uint8_t *)code->cmds[i].addr);
85 break;
86 case CMD_MEM_READ_2:
87 dstval = *((uint16_t *)code->cmds[i].addr);
88 break;
89 case CMD_MEM_READ_4:
90 dstval = *((uint32_t *)code->cmds[i].addr);
91 break;
92 case CMD_MEM_READ_8:
93 dstval = *((uint64_t *)code->cmds[i].addr);
94 break;
95 case CMD_MEM_WRITE_1:
96 *((uint8_t *)code->cmds[i].addr) = code->cmds[i].value;
97 break;
98 case CMD_MEM_WRITE_2:
99 *((uint16_t *)code->cmds[i].addr) = code->cmds[i].value;
100 break;
101 case CMD_MEM_WRITE_4:
102 *((uint32_t *)code->cmds[i].addr) = code->cmds[i].value;
103 break;
104 case CMD_MEM_WRITE_8:
105 *((uint64_t *)code->cmds[i].addr) = code->cmds[i].value;
106 break;
107#if defined(ia32) || defined(amd64)
108 case CMD_PORT_READ_1:
109 dstval = inb((long)code->cmds[i].addr);
110 break;
111 case CMD_PORT_WRITE_1:
112 outb((long)code->cmds[i].addr, code->cmds[i].value);
113 break;
114#endif
115#if defined(ia64)
116 case CMD_IA64_GETCHAR:
117 dstval = _getc(&ski_uconsole);
118 break;
119#endif
120#if defined(ppc32)
121 case CMD_PPC32_GETCHAR:
122 dstval = cuda_get_scancode();
123 break;
124#endif
125 default:
126 break;
127 }
128 if (code->cmds[i].dstarg && code->cmds[i].dstarg < 4) {
129 call->data.args[code->cmds[i].dstarg] = dstval;
130 }
131 }
132}
133
134static void code_free(irq_code_t *code)
135{
136 if (code) {
137 free(code->cmds);
138 free(code);
139 }
140}
141
142static irq_code_t * code_from_uspace(irq_code_t *ucode)
143{
144 irq_code_t *code;
145 irq_cmd_t *ucmds;
146 int rc;
147
148 code = malloc(sizeof(*code), 0);
149 rc = copy_from_uspace(code, ucode, sizeof(*code));
150 if (rc != 0) {
151 free(code);
152 return NULL;
153 }
154
155 if (code->cmdcount > IRQ_MAX_PROG_SIZE) {
156 free(code);
157 return NULL;
158 }
159 ucmds = code->cmds;
160 code->cmds = malloc(sizeof(code->cmds[0]) * (code->cmdcount), 0);
161 rc = copy_from_uspace(code->cmds, ucmds, sizeof(code->cmds[0]) * (code->cmdcount));
162 if (rc != 0) {
163 free(code->cmds);
164 free(code);
165 return NULL;
166 }
167
168 return code;
169}
170
171/** Unregister task from irq */
172void ipc_irq_unregister(answerbox_t *box, int irq)
173{
174 ipl_t ipl;
175 int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
176
177 ipl = interrupts_disable();
178 spinlock_lock(&irq_conns[mq].lock);
179 if (irq_conns[mq].box == box) {
180 irq_conns[mq].box = NULL;
181 code_free(irq_conns[mq].code);
182 irq_conns[mq].code = NULL;
183 }
184
185 spinlock_unlock(&irq_conns[mq].lock);
186 interrupts_restore(ipl);
187}
188
189/** Register an answerbox as a receiving end of interrupts notifications */
190int ipc_irq_register(answerbox_t *box, int irq, irq_code_t *ucode)
191{
192 ipl_t ipl;
193 irq_code_t *code;
194 int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
195
196 ASSERT(irq_conns);
197
198 if (ucode) {
199 code = code_from_uspace(ucode);
200 if (!code)
201 return EBADMEM;
202 } else
203 code = NULL;
204
205 ipl = interrupts_disable();
206 spinlock_lock(&irq_conns[mq].lock);
207
208 if (irq_conns[mq].box) {
209 spinlock_unlock(&irq_conns[mq].lock);
210 interrupts_restore(ipl);
211 code_free(code);
212 return EEXISTS;
213 }
214 irq_conns[mq].box = box;
215 irq_conns[mq].code = code;
216 atomic_set(&irq_conns[mq].counter, 0);
217 spinlock_unlock(&irq_conns[mq].lock);
218 interrupts_restore(ipl);
219
220 return 0;
221}
222
223/** Add call to proper answerbox queue
224 *
225 * Assume irq_conns[mq].lock is locked */
226static void send_call(int mq, call_t *call)
227{
228 spinlock_lock(&irq_conns[mq].box->irq_lock);
229 list_append(&call->link, &irq_conns[mq].box->irq_notifs);
230 spinlock_unlock(&irq_conns[mq].box->irq_lock);
231
232 waitq_wakeup(&irq_conns[mq].box->wq, 0);
233}
234
235/** Send notification message
236 *
237 */
238void ipc_irq_send_msg(int irq, unative_t a1, unative_t a2, unative_t a3)
239{
240 call_t *call;
241 int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
242
243 spinlock_lock(&irq_conns[mq].lock);
244
245 if (irq_conns[mq].box) {
246 call = ipc_call_alloc(FRAME_ATOMIC);
247 if (!call) {
248 spinlock_unlock(&irq_conns[mq].lock);
249 return;
250 }
251 call->flags |= IPC_CALL_NOTIF;
252 IPC_SET_METHOD(call->data, irq);
253 IPC_SET_ARG1(call->data, a1);
254 IPC_SET_ARG2(call->data, a2);
255 IPC_SET_ARG3(call->data, a3);
256 /* Put a counter to the message */
257 call->private = atomic_preinc(&irq_conns[mq].counter);
258
259 send_call(mq, call);
260 }
261 spinlock_unlock(&irq_conns[mq].lock);
262}
263
264/** Notify task that an irq had occurred.
265 *
266 * We expect interrupts to be disabled
267 */
268void ipc_irq_send_notif(int irq)
269{
270 call_t *call;
271 int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
272
273 ASSERT(irq_conns);
274 spinlock_lock(&irq_conns[mq].lock);
275
276 if (irq_conns[mq].box) {
277 call = ipc_call_alloc(FRAME_ATOMIC);
278 if (!call) {
279 spinlock_unlock(&irq_conns[mq].lock);
280 return;
281 }
282 call->flags |= IPC_CALL_NOTIF;
283 /* Put a counter to the message */
284 call->private = atomic_preinc(&irq_conns[mq].counter);
285 /* Set up args */
286 IPC_SET_METHOD(call->data, irq);
287
288 /* Execute code to handle irq */
289 code_execute(call, irq_conns[mq].code);
290
291 send_call(mq, call);
292 }
293
294 spinlock_unlock(&irq_conns[mq].lock);
295}
296
297
298/** Initialize table of interrupt handlers
299 *
300 * @param irqcount Count of required hardware IRQs to be supported
301 */
302void ipc_irq_make_table(int irqcount)
303{
304 int i;
305
306 irqcount += IPC_IRQ_RESERVED_VIRTUAL;
307
308 irq_conns_size = irqcount;
309 irq_conns = malloc(irqcount * (sizeof(*irq_conns)), 0);
310 for (i=0; i < irqcount; i++) {
311 spinlock_initialize(&irq_conns[i].lock, "irq_ipc_lock");
312 irq_conns[i].box = NULL;
313 irq_conns[i].code = NULL;
314 }
315}
316
317/** Disconnect all irq's notifications
318 *
319 * @todo It may be better to do some linked list, so that
320 * we wouldn't need to go through whole array every cleanup
321 */
322void ipc_irq_cleanup(answerbox_t *box)
323{
324 int i;
325 ipl_t ipl;
326
327 for (i=0; i < irq_conns_size; i++) {
328 ipl = interrupts_disable();
329 spinlock_lock(&irq_conns[i].lock);
330 if (irq_conns[i].box == box)
331 irq_conns[i].box = NULL;
332 spinlock_unlock(&irq_conns[i].lock);
333 interrupts_restore(ipl);
334 }
335}
336
337/** @}
338 */
Note: See TracBrowser for help on using the repository browser.