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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 253f35a1 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
RevLine 
[162f919]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
[cc73a8a1]29/** @addtogroup genericipc
[b45c443]30 * @{
31 */
[cc73a8a1]32/**
33 * @file
34 * @brief IRQ notification framework.
[bdc5c516]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:
[43752b6]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
[bdc5c516]47 * in multithreaded drivers)
48 */
49
[162f919]50#include <arch.h>
51#include <mm/slab.h>
52#include <errno.h>
53#include <ipc/ipc.h>
54#include <ipc/irq.h>
[bdc5c516]55#include <atomic.h>
[e3c762cd]56#include <syscall/copy.h>
[d0c5901]57#include <console/console.h>
[253f35a1]58#include <print.h>
[162f919]59
60typedef struct {
61 SPINLOCK_DECLARE(lock);
62 answerbox_t *box;
63 irq_code_t *code;
[bdc5c516]64 atomic_t counter;
[162f919]65} ipc_irq_t;
66
67
68static ipc_irq_t *irq_conns = NULL;
69static int irq_conns_size;
70
[253f35a1]71
[162f919]72/* Execute code associated with IRQ notification */
73static void code_execute(call_t *call, irq_code_t *code)
74{
75 int i;
[7f1c620]76 unative_t dstval = 0;
[ae971b3e]77
[162f919]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:
[7f1c620]84 dstval = *((uint8_t *)code->cmds[i].addr);
[162f919]85 break;
86 case CMD_MEM_READ_2:
[7f1c620]87 dstval = *((uint16_t *)code->cmds[i].addr);
[162f919]88 break;
89 case CMD_MEM_READ_4:
[7f1c620]90 dstval = *((uint32_t *)code->cmds[i].addr);
[162f919]91 break;
92 case CMD_MEM_READ_8:
[7f1c620]93 dstval = *((uint64_t *)code->cmds[i].addr);
[162f919]94 break;
95 case CMD_MEM_WRITE_1:
[7f1c620]96 *((uint8_t *)code->cmds[i].addr) = code->cmds[i].value;
[162f919]97 break;
98 case CMD_MEM_WRITE_2:
[7f1c620]99 *((uint16_t *)code->cmds[i].addr) = code->cmds[i].value;
[162f919]100 break;
101 case CMD_MEM_WRITE_4:
[7f1c620]102 *((uint32_t *)code->cmds[i].addr) = code->cmds[i].value;
[162f919]103 break;
104 case CMD_MEM_WRITE_8:
[7f1c620]105 *((uint64_t *)code->cmds[i].addr) = code->cmds[i].value;
[162f919]106 break;
[bdc5c516]107#if defined(ia32) || defined(amd64)
108 case CMD_PORT_READ_1:
[43752b6]109 dstval = inb((long)code->cmds[i].addr);
[bdc5c516]110 break;
111 case CMD_PORT_WRITE_1:
112 outb((long)code->cmds[i].addr, code->cmds[i].value);
113 break;
[d0c5901]114#endif
115#if defined(ia64)
116 case CMD_IA64_GETCHAR:
[43752b6]117 dstval = _getc(&ski_uconsole);
[d0c5901]118 break;
[732fd3c]119#endif
[ae971b3e]120#if defined(ppc32)
[732fd3c]121 case CMD_PPC32_GETCHAR:
[43752b6]122 dstval = cuda_get_scancode();
[732fd3c]123 break;
[bdc5c516]124#endif
[162f919]125 default:
126 break;
127 }
[43752b6]128 if (code->cmds[i].dstarg && code->cmds[i].dstarg < 4) {
129 call->data.args[code->cmds[i].dstarg] = dstval;
130 }
[162f919]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;
[e3c762cd]146 int rc;
[162f919]147
148 code = malloc(sizeof(*code), 0);
[e3c762cd]149 rc = copy_from_uspace(code, ucode, sizeof(*code));
150 if (rc != 0) {
151 free(code);
152 return NULL;
153 }
[162f919]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);
[e3c762cd]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 }
[162f919]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;
[874621f]175 int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
[162f919]176
177 ipl = interrupts_disable();
[874621f]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;
[162f919]183 }
184
[874621f]185 spinlock_unlock(&irq_conns[mq].lock);
[162f919]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;
[874621f]194 int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
[162f919]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();
[874621f]206 spinlock_lock(&irq_conns[mq].lock);
[162f919]207
[874621f]208 if (irq_conns[mq].box) {
209 spinlock_unlock(&irq_conns[mq].lock);
[162f919]210 interrupts_restore(ipl);
211 code_free(code);
212 return EEXISTS;
213 }
[874621f]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);
[162f919]218 interrupts_restore(ipl);
219
220 return 0;
221}
222
[874621f]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 */
[7f1c620]238void ipc_irq_send_msg(int irq, unative_t a1, unative_t a2, unative_t a3)
[874621f]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;
[43752b6]252 IPC_SET_METHOD(call->data, irq);
253 IPC_SET_ARG1(call->data, a1);
[874621f]254 IPC_SET_ARG2(call->data, a2);
255 IPC_SET_ARG3(call->data, a3);
[43752b6]256 /* Put a counter to the message */
257 call->private = atomic_preinc(&irq_conns[mq].counter);
[874621f]258
259 send_call(mq, call);
260 }
261 spinlock_unlock(&irq_conns[mq].lock);
262}
263
[f3a3f0d7]264/** Notify task that an irq had occurred.
[162f919]265 *
266 * We expect interrupts to be disabled
267 */
268void ipc_irq_send_notif(int irq)
269{
270 call_t *call;
[874621f]271 int mq = irq + IPC_IRQ_RESERVED_VIRTUAL;
[162f919]272
273 ASSERT(irq_conns);
[874621f]274 spinlock_lock(&irq_conns[mq].lock);
[162f919]275
[874621f]276 if (irq_conns[mq].box) {
[162f919]277 call = ipc_call_alloc(FRAME_ATOMIC);
[d8f7362]278 if (!call) {
[874621f]279 spinlock_unlock(&irq_conns[mq].lock);
[d8f7362]280 return;
281 }
[162f919]282 call->flags |= IPC_CALL_NOTIF;
[43752b6]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);
[162f919]287
288 /* Execute code to handle irq */
[874621f]289 code_execute(call, irq_conns[mq].code);
290
291 send_call(mq, call);
[162f919]292 }
293
[874621f]294 spinlock_unlock(&irq_conns[mq].lock);
[162f919]295}
296
297
[874621f]298/** Initialize table of interrupt handlers
299 *
300 * @param irqcount Count of required hardware IRQs to be supported
301 */
[162f919]302void ipc_irq_make_table(int irqcount)
303{
304 int i;
305
[874621f]306 irqcount += IPC_IRQ_RESERVED_VIRTUAL;
307
[162f919]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 *
[cc73a8a1]319 * @todo It may be better to do some linked list, so that
[162f919]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}
[b45c443]336
[cc73a8a1]337/** @}
[b45c443]338 */
Note: See TracBrowser for help on using the repository browser.