source: mainline/generic/src/ipc/irq.c@ 22cf454d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 22cf454d was bdc5c516, checked in by Ondrej Palkovsky <ondrap@…>, 20 years ago

Added commands for accessing ia32 portspace in irq top-half.

  • Property mode set to 100644
File size: 6.6 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
[bdc5c516]29/** IRQ notification framework
30 *
31 * This framework allows applications to register to receive a notification
32 * when interrupt is detected. The application may provide a simple 'top-half'
33 * handler as part of its registration, which can perform simple operations
34 * (read/write port/memory, add information to notification ipc message).
35 *
36 * The structure of a notification message is as follows:
37 * - METHOD: IPC_M_INTERRUPT
38 * - ARG1: interrupt number
39 * - ARG2: payload modified by a 'top-half' handler
40 * - ARG3: interrupt counter (may be needed to assure correct order
41 * in multithreaded drivers)
42 */
43
[162f919]44#include <arch.h>
45#include <mm/slab.h>
46#include <errno.h>
47#include <ipc/ipc.h>
48#include <ipc/irq.h>
[bdc5c516]49#include <atomic.h>
[162f919]50
51typedef struct {
52 SPINLOCK_DECLARE(lock);
53 answerbox_t *box;
54 irq_code_t *code;
[bdc5c516]55 atomic_t counter;
[162f919]56} ipc_irq_t;
57
58
59static ipc_irq_t *irq_conns = NULL;
60static int irq_conns_size;
61
62#include <print.h>
63/* Execute code associated with IRQ notification */
64static void code_execute(call_t *call, irq_code_t *code)
65{
66 int i;
67
68 if (!code)
69 return;
70
71 for (i=0; i < code->cmdcount;i++) {
72 switch (code->cmds[i].cmd) {
73 case CMD_MEM_READ_1:
74 IPC_SET_ARG2(call->data, *((__u8 *)code->cmds[i].addr));
75 break;
76 case CMD_MEM_READ_2:
77 IPC_SET_ARG2(call->data, *((__u16 *)code->cmds[i].addr));
78 break;
79 case CMD_MEM_READ_4:
80 IPC_SET_ARG2(call->data, *((__u32 *)code->cmds[i].addr));
81 break;
82 case CMD_MEM_READ_8:
83 IPC_SET_ARG2(call->data, *((__u64 *)code->cmds[i].addr));
84 break;
85 case CMD_MEM_WRITE_1:
86 *((__u8 *)code->cmds[i].addr) = code->cmds[i].value;
87 break;
88 case CMD_MEM_WRITE_2:
89 *((__u16 *)code->cmds[i].addr) = code->cmds[i].value;
90 break;
91 case CMD_MEM_WRITE_4:
92 *((__u32 *)code->cmds[i].addr) = code->cmds[i].value;
93 break;
94 case CMD_MEM_WRITE_8:
95 *((__u64 *)code->cmds[i].addr) = code->cmds[i].value;
96 break;
[bdc5c516]97#if defined(ia32) || defined(amd64)
98 case CMD_PORT_READ_1:
99 IPC_SET_ARG2(call->data, inb((long)code->cmds[i].addr));
100 break;
101 case CMD_PORT_WRITE_1:
102 outb((long)code->cmds[i].addr, code->cmds[i].value);
103 break;
104#endif
[162f919]105 default:
106 break;
107 }
108 }
109}
110
111static void code_free(irq_code_t *code)
112{
113 if (code) {
114 free(code->cmds);
115 free(code);
116 }
117}
118
119static irq_code_t * code_from_uspace(irq_code_t *ucode)
120{
121 irq_code_t *code;
122 irq_cmd_t *ucmds;
123
124 code = malloc(sizeof(*code), 0);
125 copy_from_uspace(code, ucode, sizeof(*code));
126
127 if (code->cmdcount > IRQ_MAX_PROG_SIZE) {
128 free(code);
129 return NULL;
130 }
131 ucmds = code->cmds;
132 code->cmds = malloc(sizeof(code->cmds[0]) * (code->cmdcount), 0);
133 copy_from_uspace(code->cmds, ucmds, sizeof(code->cmds[0]) * (code->cmdcount));
134
135 return code;
136}
137
138/** Unregister task from irq */
139void ipc_irq_unregister(answerbox_t *box, int irq)
140{
141 ipl_t ipl;
142
143 ipl = interrupts_disable();
144 spinlock_lock(&irq_conns[irq].lock);
145 if (irq_conns[irq].box == box) {
146 irq_conns[irq].box = NULL;
147 code_free(irq_conns[irq].code);
148 irq_conns[irq].code = NULL;
149 }
150
151 spinlock_unlock(&irq_conns[irq].lock);
152 interrupts_restore(ipl);
153}
154
155/** Register an answerbox as a receiving end of interrupts notifications */
156int ipc_irq_register(answerbox_t *box, int irq, irq_code_t *ucode)
157{
158 ipl_t ipl;
159 irq_code_t *code;
160
161 ASSERT(irq_conns);
162
163 if (ucode) {
164 code = code_from_uspace(ucode);
165 if (!code)
166 return EBADMEM;
167 } else
168 code = NULL;
169
170 ipl = interrupts_disable();
171 spinlock_lock(&irq_conns[irq].lock);
172
173 if (irq_conns[irq].box) {
174 spinlock_unlock(&irq_conns[irq].lock);
175 interrupts_restore(ipl);
176 code_free(code);
177 return EEXISTS;
178 }
179 irq_conns[irq].box = box;
180 irq_conns[irq].code = code;
[bdc5c516]181 atomic_set(&irq_conns[irq].counter, 0);
[162f919]182 spinlock_unlock(&irq_conns[irq].lock);
183 interrupts_restore(ipl);
184
185 return 0;
186}
187
188/** Notify process that an irq had happend
189 *
190 * We expect interrupts to be disabled
191 */
192void ipc_irq_send_notif(int irq)
193{
194 call_t *call;
195
196 ASSERT(irq_conns);
197 spinlock_lock(&irq_conns[irq].lock);
198
199 if (irq_conns[irq].box) {
200 call = ipc_call_alloc(FRAME_ATOMIC);
201 call->flags |= IPC_CALL_NOTIF;
202 IPC_SET_METHOD(call->data, IPC_M_INTERRUPT);
203 IPC_SET_ARG1(call->data, irq);
[bdc5c516]204 IPC_SET_ARG3(call->data, atomic_preinc(&irq_conns[irq].counter));
[162f919]205
206 /* Execute code to handle irq */
207 code_execute(call, irq_conns[irq].code);
208
209 spinlock_lock(&irq_conns[irq].box->irq_lock);
210 list_append(&call->list, &irq_conns[irq].box->irq_notifs);
211 spinlock_unlock(&irq_conns[irq].box->irq_lock);
212
213 waitq_wakeup(&irq_conns[irq].box->wq, 0);
214 }
215
216 spinlock_unlock(&irq_conns[irq].lock);
217}
218
219
220/** Initialize table of interrupt handlers */
221void ipc_irq_make_table(int irqcount)
222{
223 int i;
224
225 irq_conns_size = irqcount;
226 irq_conns = malloc(irqcount * (sizeof(*irq_conns)), 0);
227 for (i=0; i < irqcount; i++) {
228 spinlock_initialize(&irq_conns[i].lock, "irq_ipc_lock");
229 irq_conns[i].box = NULL;
230 irq_conns[i].code = NULL;
231 }
232}
233
234/** Disconnect all irq's notifications
235 *
236 * TODO: It may be better to do some linked list, so that
237 * we wouldn't need to go through whole array every cleanup
238 */
239void ipc_irq_cleanup(answerbox_t *box)
240{
241 int i;
242 ipl_t ipl;
243
244 for (i=0; i < irq_conns_size; i++) {
245 ipl = interrupts_disable();
246 spinlock_lock(&irq_conns[i].lock);
247 if (irq_conns[i].box == box)
248 irq_conns[i].box = NULL;
249 spinlock_unlock(&irq_conns[i].lock);
250 interrupts_restore(ipl);
251 }
252}
Note: See TracBrowser for help on using the repository browser.