source: mainline/generic/src/ipc/irq.c@ 41d33ac

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

Complete implementation of copy_from_uspace() and copy_to_uspace()
for amd64 and ia32. Other architectures still compile and run,
but need to implement their own assembly-only memcpy(), memcpy_from_uspace(),
memcpy_to_uspace() and their failover parts. For these architectures
only dummy implementations are provided.

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