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

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

Do not copy notification config out of the IRQ structure.
Add the notify member, that toggles notifications on and off instead.
This is good for preventing other tasks from registering the notification
while the notification config is stored outside the IRQ structure.
It should also help to implement the cleanup code.

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