source: mainline/kernel/generic/src/ipc/irq.c@ 82cbf8c6

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

Replace the old hash table implementation in the kernel with the newer one

This replaces the original hash table implementation with the resizable one
already used in uspace. Along the way, the IRQ hash table code was streamlined
and cleaned up.

  • Property mode set to 100644
File size: 14.3 KB
RevLine 
[162f919]1/*
[df4ed85]2 * Copyright (c) 2006 Ondrej Palkovsky
3 * Copyright (c) 2006 Jakub Jermar
[162f919]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
[cc73a8a1]30/** @addtogroup genericipc
[b45c443]31 * @{
32 */
[da1bafb]33
[cc73a8a1]34/**
35 * @file
36 * @brief IRQ notification framework.
[bdc5c516]37 *
[8820544]38 * This framework allows applications to subscribe to receive a notification
[a5d0143]39 * when an interrupt is detected. The application may provide a simple
40 * 'top-half' handler as part of its registration, which can perform simple
41 * operations (read/write port/memory, add information to notification IPC
42 * message).
[bdc5c516]43 *
44 * The structure of a notification message is as follows:
[8820544]45 * - IMETHOD: interface and method as set by the SYS_IPC_IRQ_SUBSCRIBE syscall
[56c167c]46 * - ARG1: payload modified by a 'top-half' handler (scratch[1])
47 * - ARG2: payload modified by a 'top-half' handler (scratch[2])
48 * - ARG3: payload modified by a 'top-half' handler (scratch[3])
49 * - ARG4: payload modified by a 'top-half' handler (scratch[4])
50 * - ARG5: payload modified by a 'top-half' handler (scratch[5])
[43752b6]51 * - in_phone_hash: interrupt counter (may be needed to assure correct order
[228e490]52 * in multithreaded drivers)
[bdc5c516]53 */
54
[162f919]55#include <arch.h>
[63e27ef]56#include <assert.h>
[162f919]57#include <mm/slab.h>
[a996ae31]58#include <mm/page.h>
59#include <mm/km.h>
[162f919]60#include <errno.h>
[2b017ba]61#include <ddi/irq.h>
[162f919]62#include <ipc/ipc.h>
63#include <ipc/irq.h>
[e3c762cd]64#include <syscall/copy.h>
[d0c5901]65#include <console/console.h>
[253f35a1]66#include <print.h>
[a996ae31]67#include <macros.h>
[3f74275]68#include <cap/cap.h>
[a996ae31]69
70static void ranges_unmap(irq_pio_range_t *ranges, size_t rangecount)
71{
[56c167c]72 for (size_t i = 0; i < rangecount; i++) {
[472d813]73#ifdef IO_SPACE_BOUNDARY
[a996ae31]74 if ((void *) ranges[i].base >= IO_SPACE_BOUNDARY)
[472d813]75#endif
[a996ae31]76 km_unmap(ranges[i].base, ranges[i].size);
77 }
78}
79
80static int ranges_map_and_apply(irq_pio_range_t *ranges, size_t rangecount,
81 irq_cmd_t *cmds, size_t cmdcount)
82{
83 /* Copy the physical base addresses aside. */
[56c167c]84 uintptr_t *pbase = malloc(rangecount * sizeof(uintptr_t), 0);
85 for (size_t i = 0; i < rangecount; i++)
[a996ae31]86 pbase[i] = ranges[i].base;
[56c167c]87
[a996ae31]88 /* Map the PIO ranges into the kernel virtual address space. */
[56c167c]89 for (size_t i = 0; i < rangecount; i++) {
[472d813]90#ifdef IO_SPACE_BOUNDARY
[a996ae31]91 if ((void *) ranges[i].base < IO_SPACE_BOUNDARY)
92 continue;
[472d813]93#endif
[a996ae31]94 ranges[i].base = km_map(pbase[i], ranges[i].size,
95 PAGE_READ | PAGE_WRITE | PAGE_KERNEL | PAGE_NOT_CACHEABLE);
96 if (!ranges[i].base) {
97 ranges_unmap(ranges, i);
98 free(pbase);
99 return ENOMEM;
100 }
101 }
[56c167c]102
[a5d0143]103 /* Rewrite the IRQ code addresses from physical to kernel virtual. */
[56c167c]104 for (size_t i = 0; i < cmdcount; i++) {
[a996ae31]105 uintptr_t addr;
[f2bbe8c]106 size_t size;
[56c167c]107
[a996ae31]108 /* Process only commands that use an address. */
109 switch (cmds[i].cmd) {
110 case CMD_PIO_READ_8:
[56c167c]111 case CMD_PIO_WRITE_8:
112 case CMD_PIO_WRITE_A_8:
[f2bbe8c]113 size = 1;
114 break;
[56c167c]115 case CMD_PIO_READ_16:
116 case CMD_PIO_WRITE_16:
117 case CMD_PIO_WRITE_A_16:
[f2bbe8c]118 size = 2;
119 break;
[56c167c]120 case CMD_PIO_READ_32:
121 case CMD_PIO_WRITE_32:
122 case CMD_PIO_WRITE_A_32:
[f2bbe8c]123 size = 4;
[a996ae31]124 break;
125 default:
126 /* Move onto the next command. */
127 continue;
128 }
[56c167c]129
[a996ae31]130 addr = (uintptr_t) cmds[i].addr;
131
[56c167c]132 size_t j;
[a996ae31]133 for (j = 0; j < rangecount; j++) {
134 /* Find the matching range. */
[f2bbe8c]135 if (!iswithin(pbase[j], ranges[j].size, addr, size))
[a996ae31]136 continue;
[56c167c]137
[a996ae31]138 /* Switch the command to a kernel virtual address. */
139 addr -= pbase[j];
140 addr += ranges[j].base;
[56c167c]141
[a996ae31]142 cmds[i].addr = (void *) addr;
143 break;
[bd8c6537]144 }
[56c167c]145
[bd8c6537]146 if (j == rangecount) {
147 /*
148 * The address used in this command is outside of all
149 * defined ranges.
150 */
151 ranges_unmap(ranges, rangecount);
152 free(pbase);
153 return EINVAL;
154 }
[a996ae31]155 }
[56c167c]156
[a996ae31]157 free(pbase);
158 return EOK;
159}
[162f919]160
[a5d0143]161/** Statically check the top-half IRQ code
[8486c07]162 *
[a5d0143]163 * Check the top-half IRQ code for invalid or unsafe constructs.
[8486c07]164 *
165 */
166static int code_check(irq_cmd_t *cmds, size_t cmdcount)
167{
168 for (size_t i = 0; i < cmdcount; i++) {
169 /*
170 * Check for accepted ranges.
171 */
172 if (cmds[i].cmd >= CMD_LAST)
173 return EINVAL;
174
175 if (cmds[i].srcarg >= IPC_CALL_LEN)
176 return EINVAL;
177
178 if (cmds[i].dstarg >= IPC_CALL_LEN)
179 return EINVAL;
180
181 switch (cmds[i].cmd) {
182 case CMD_PREDICATE:
183 /*
184 * Check for control flow overflow.
185 * Note that jumping just beyond the last
186 * command is a correct behaviour.
187 */
188 if (i + cmds[i].value > cmdcount)
189 return EINVAL;
190
191 break;
192 default:
193 break;
194 }
195 }
196
197 return EOK;
198}
199
[a5d0143]200/** Free the top-half IRQ code.
[8b243f2]201 *
[a5d0143]202 * @param code Pointer to the top-half IRQ code.
[da1bafb]203 *
[8b243f2]204 */
[162f919]205static void code_free(irq_code_t *code)
206{
207 if (code) {
[a996ae31]208 ranges_unmap(code->ranges, code->rangecount);
209 free(code->ranges);
[162f919]210 free(code->cmds);
211 free(code);
212 }
213}
214
[a5d0143]215/** Copy the top-half IRQ code from userspace into the kernel.
[8b243f2]216 *
[a5d0143]217 * @param ucode Userspace address of the top-half IRQ code.
[da1bafb]218 *
[a5d0143]219 * @return Kernel address of the copied IRQ code.
[8b243f2]220 *
221 */
222static irq_code_t *code_from_uspace(irq_code_t *ucode)
[162f919]223{
[a996ae31]224 irq_pio_range_t *ranges = NULL;
225 irq_cmd_t *cmds = NULL;
[56c167c]226
[da1bafb]227 irq_code_t *code = malloc(sizeof(*code), 0);
228 int rc = copy_from_uspace(code, ucode, sizeof(*code));
[a996ae31]229 if (rc != EOK)
230 goto error;
[162f919]231
[a996ae31]232 if ((code->rangecount > IRQ_MAX_RANGE_COUNT) ||
233 (code->cmdcount > IRQ_MAX_PROG_SIZE))
234 goto error;
[da1bafb]235
[a996ae31]236 ranges = malloc(sizeof(code->ranges[0]) * code->rangecount, 0);
237 rc = copy_from_uspace(ranges, code->ranges,
238 sizeof(code->ranges[0]) * code->rangecount);
239 if (rc != EOK)
240 goto error;
[56c167c]241
[a996ae31]242 cmds = malloc(sizeof(code->cmds[0]) * code->cmdcount, 0);
243 rc = copy_from_uspace(cmds, code->cmds,
[8b243f2]244 sizeof(code->cmds[0]) * code->cmdcount);
[a996ae31]245 if (rc != EOK)
246 goto error;
[8486c07]247
248 rc = code_check(cmds, code->cmdcount);
249 if (rc != EOK)
250 goto error;
251
[a996ae31]252 rc = ranges_map_and_apply(ranges, code->rangecount, cmds,
253 code->cmdcount);
254 if (rc != EOK)
255 goto error;
[56c167c]256
[a996ae31]257 code->ranges = ranges;
258 code->cmds = cmds;
[56c167c]259
[162f919]260 return code;
[56c167c]261
[a996ae31]262error:
263 if (cmds)
264 free(cmds);
[56c167c]265
[a996ae31]266 if (ranges)
267 free(ranges);
[56c167c]268
[a996ae31]269 free(code);
270 return NULL;
[162f919]271}
272
[48bcf49]273static void irq_destroy(void *arg)
274{
275 irq_t *irq = (irq_t *) arg;
276
277 /* Free up the IRQ code and associated structures. */
278 code_free(irq->notif_cfg.code);
279 slab_free(irq_slab, irq);
280}
281
282static kobject_ops_t irq_kobject_ops = {
283 .destroy = irq_destroy
284};
285
[8820544]286/** Subscribe an answerbox as a receiving end for IRQ notifications.
[2b017ba]287 *
[56c167c]288 * @param box Receiving answerbox.
289 * @param inr IRQ number.
[a5d0143]290 * @param imethod Interface and method to be associated with the notification.
291 * @param ucode Uspace pointer to top-half IRQ code.
[56c167c]292 *
[3f74275]293 * @return IRQ capability handle.
[e9d15d9]294 * @return Negative error code.
[2b017ba]295 *
296 */
[24abb85d]297int ipc_irq_subscribe(answerbox_t *box, inr_t inr, sysarg_t imethod,
298 irq_code_t *ucode)
[162f919]299{
[78ffb70]300 if ((inr < 0) || (inr > last_inr))
301 return ELIMIT;
[c822026]302
[da1bafb]303 irq_code_t *code;
[162f919]304 if (ucode) {
305 code = code_from_uspace(ucode);
306 if (!code)
307 return EBADMEM;
[da1bafb]308 } else
[162f919]309 code = NULL;
[c822026]310
[cecb0789]311 /*
[e9d15d9]312 * Allocate and populate the IRQ kernel object.
[cecb0789]313 */
[48bcf49]314 cap_handle_t handle = cap_alloc(TASK);
[3f74275]315 if (handle < 0)
316 return handle;
[63d8f43]317
[431c402]318 irq_t *irq = (irq_t *) slab_alloc(irq_slab, FRAME_ATOMIC);
[63d8f43]319 if (!irq) {
320 cap_free(TASK, handle);
321 return ENOMEM;
322 }
[48bcf49]323
324 kobject_t *kobject = malloc(sizeof(kobject_t), FRAME_ATOMIC);
325 if (!kobject) {
326 cap_free(TASK, handle);
327 slab_free(irq_slab, irq);
328 return ENOMEM;
329 }
[63d8f43]330
[cecb0789]331 irq_initialize(irq);
332 irq->inr = inr;
333 irq->claim = ipc_irq_top_half_claim;
[691eb52]334 irq->handler = ipc_irq_top_half_handler;
[4874c2d]335 irq->notif_cfg.notify = true;
[2b017ba]336 irq->notif_cfg.answerbox = box;
[228e490]337 irq->notif_cfg.imethod = imethod;
[2b017ba]338 irq->notif_cfg.code = code;
339 irq->notif_cfg.counter = 0;
[c822026]340
[cecb0789]341 /*
[9e87562]342 * Insert the IRQ structure into the uspace IRQ hash table.
[cecb0789]343 */
[da1bafb]344 irq_spinlock_lock(&irq_uspace_hash_table_lock, true);
345 irq_spinlock_lock(&irq->lock, false);
346
[48bcf49]347 irq->notif_cfg.hashed_in = true;
[82cbf8c6]348 hash_table_insert(&irq_uspace_hash_table, &irq->link);
[c822026]349
[da1bafb]350 irq_spinlock_unlock(&irq->lock, false);
351 irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
[9e87562]352
[48bcf49]353 kobject_initialize(kobject, KOBJECT_TYPE_IRQ, irq, &irq_kobject_ops);
354 cap_publish(TASK, handle, kobject);
[da1bafb]355
[3f74275]356 return handle;
[cecb0789]357}
358
[8820544]359/** Unsubscribe task from IRQ notification.
[cecb0789]360 *
[3f74275]361 * @param box Answerbox associated with the notification.
362 * @param handle IRQ capability handle.
[56c167c]363 *
364 * @return EOK on success or a negative error code.
365 *
[cecb0789]366 */
[3f74275]367int ipc_irq_unsubscribe(answerbox_t *box, int handle)
[cecb0789]368{
[48bcf49]369 kobject_t *kobj = cap_unpublish(TASK, handle, KOBJECT_TYPE_IRQ);
370 if (!kobj)
[cecb0789]371 return ENOENT;
[9306cd7]372
[48bcf49]373 assert(kobj->irq->notif_cfg.answerbox == box);
374
[e9d15d9]375 irq_spinlock_lock(&irq_uspace_hash_table_lock, true);
[48bcf49]376 irq_spinlock_lock(&kobj->irq->lock, false);
[cecb0789]377
[48bcf49]378 if (kobj->irq->notif_cfg.hashed_in) {
379 /* Remove the IRQ from the uspace IRQ hash table. */
380 hash_table_remove_item(&irq_uspace_hash_table,
381 &kobj->irq->link);
382 kobj->irq->notif_cfg.hashed_in = false;
383 }
384
[82cbf8c6]385 irq_spinlock_unlock(&kobj->irq->lock, false);
[da1bafb]386 irq_spinlock_unlock(&irq_uspace_hash_table_lock, true);
[48bcf49]387
388 kobject_put(kobj);
[3f74275]389 cap_free(TASK, handle);
[cecb0789]390
391 return EOK;
392}
393
[8b243f2]394/** Add a call to the proper answerbox queue.
[2b017ba]395 *
[da1bafb]396 * Assume irq->lock is locked and interrupts disabled.
397 *
398 * @param irq IRQ structure referencing the target answerbox.
399 * @param call IRQ notification call.
[874621f]400 *
[2b017ba]401 */
402static void send_call(irq_t *irq, call_t *call)
[874621f]403{
[da1bafb]404 irq_spinlock_lock(&irq->notif_cfg.answerbox->irq_lock, false);
[cfaa35a]405 list_append(&call->ab_link, &irq->notif_cfg.answerbox->irq_notifs);
[da1bafb]406 irq_spinlock_unlock(&irq->notif_cfg.answerbox->irq_lock, false);
407
[2b017ba]408 waitq_wakeup(&irq->notif_cfg.answerbox->wq, WAKEUP_FIRST);
[874621f]409}
410
[a5d0143]411/** Apply the top-half IRQ code to find out whether to accept the IRQ or not.
[874621f]412 *
[da1bafb]413 * @param irq IRQ structure.
414 *
[a5d0143]415 * @return IRQ_ACCEPT if the interrupt is accepted by the IRQ code.
416 * @return IRQ_DECLINE if the interrupt is not accepted byt the IRQ code.
[cecb0789]417 *
[874621f]418 */
[cecb0789]419irq_ownership_t ipc_irq_top_half_claim(irq_t *irq)
[874621f]420{
[cecb0789]421 irq_code_t *code = irq->notif_cfg.code;
[da1bafb]422 uint32_t *scratch = irq->notif_cfg.scratch;
[cecb0789]423
424 if (!irq->notif_cfg.notify)
425 return IRQ_DECLINE;
426
427 if (!code)
428 return IRQ_DECLINE;
429
[01e39cbe]430 for (size_t i = 0; i < code->cmdcount; i++) {
[da1bafb]431 uintptr_t srcarg = code->cmds[i].srcarg;
432 uintptr_t dstarg = code->cmds[i].dstarg;
[874621f]433
[cecb0789]434 switch (code->cmds[i].cmd) {
435 case CMD_PIO_READ_8:
[8486c07]436 scratch[dstarg] =
437 pio_read_8((ioport8_t *) code->cmds[i].addr);
[cecb0789]438 break;
439 case CMD_PIO_READ_16:
[8486c07]440 scratch[dstarg] =
441 pio_read_16((ioport16_t *) code->cmds[i].addr);
[cecb0789]442 break;
443 case CMD_PIO_READ_32:
[8486c07]444 scratch[dstarg] =
445 pio_read_32((ioport32_t *) code->cmds[i].addr);
[cecb0789]446 break;
447 case CMD_PIO_WRITE_8:
448 pio_write_8((ioport8_t *) code->cmds[i].addr,
449 (uint8_t) code->cmds[i].value);
450 break;
451 case CMD_PIO_WRITE_16:
452 pio_write_16((ioport16_t *) code->cmds[i].addr,
453 (uint16_t) code->cmds[i].value);
454 break;
455 case CMD_PIO_WRITE_32:
456 pio_write_32((ioport32_t *) code->cmds[i].addr,
457 (uint32_t) code->cmds[i].value);
458 break;
[9cdac5a]459 case CMD_PIO_WRITE_A_8:
[8486c07]460 pio_write_8((ioport8_t *) code->cmds[i].addr,
461 (uint8_t) scratch[srcarg]);
[9cdac5a]462 break;
463 case CMD_PIO_WRITE_A_16:
[8486c07]464 pio_write_16((ioport16_t *) code->cmds[i].addr,
465 (uint16_t) scratch[srcarg]);
[9cdac5a]466 break;
467 case CMD_PIO_WRITE_A_32:
[8486c07]468 pio_write_32((ioport32_t *) code->cmds[i].addr,
469 (uint32_t) scratch[srcarg]);
470 break;
471 case CMD_LOAD:
472 scratch[dstarg] = code->cmds[i].value;
[9cdac5a]473 break;
[8486c07]474 case CMD_AND:
475 scratch[dstarg] = scratch[srcarg] &
476 code->cmds[i].value;
[cecb0789]477 break;
478 case CMD_PREDICATE:
[8486c07]479 if (scratch[srcarg] == 0)
[cecb0789]480 i += code->cmds[i].value;
[8486c07]481
[cecb0789]482 break;
483 case CMD_ACCEPT:
484 return IRQ_ACCEPT;
485 case CMD_DECLINE:
486 default:
487 return IRQ_DECLINE;
488 }
[874621f]489 }
[01e39cbe]490
[cecb0789]491 return IRQ_DECLINE;
[874621f]492}
493
[cecb0789]494/* IRQ top-half handler.
[162f919]495 *
[2b017ba]496 * We expect interrupts to be disabled and the irq->lock already held.
[8b243f2]497 *
[da1bafb]498 * @param irq IRQ structure.
499 *
[162f919]500 */
[cecb0789]501void ipc_irq_top_half_handler(irq_t *irq)
[162f919]502{
[63e27ef]503 assert(irq);
[56c167c]504
[63e27ef]505 assert(interrupts_disabled());
506 assert(irq_spinlock_locked(&irq->lock));
[da1bafb]507
[2b017ba]508 if (irq->notif_cfg.answerbox) {
[da1bafb]509 call_t *call = ipc_call_alloc(FRAME_ATOMIC);
[cecb0789]510 if (!call)
[d8f7362]511 return;
[cecb0789]512
[162f919]513 call->flags |= IPC_CALL_NOTIF;
[43752b6]514 /* Put a counter to the message */
[0c1a5d8a]515 call->priv = ++irq->notif_cfg.counter;
[da1bafb]516
[43752b6]517 /* Set up args */
[228e490]518 IPC_SET_IMETHOD(call->data, irq->notif_cfg.imethod);
[cecb0789]519 IPC_SET_ARG1(call->data, irq->notif_cfg.scratch[1]);
520 IPC_SET_ARG2(call->data, irq->notif_cfg.scratch[2]);
521 IPC_SET_ARG3(call->data, irq->notif_cfg.scratch[3]);
522 IPC_SET_ARG4(call->data, irq->notif_cfg.scratch[4]);
523 IPC_SET_ARG5(call->data, irq->notif_cfg.scratch[5]);
[da1bafb]524
[2b017ba]525 send_call(irq, call);
[162f919]526 }
527}
528
[cecb0789]529/** Send notification message.
[874621f]530 *
[da1bafb]531 * @param irq IRQ structure.
532 * @param a1 Driver-specific payload argument.
533 * @param a2 Driver-specific payload argument.
534 * @param a3 Driver-specific payload argument.
535 * @param a4 Driver-specific payload argument.
536 * @param a5 Driver-specific payload argument.
537 *
[162f919]538 */
[96b02eb9]539void ipc_irq_send_msg(irq_t *irq, sysarg_t a1, sysarg_t a2, sysarg_t a3,
540 sysarg_t a4, sysarg_t a5)
[162f919]541{
[da1bafb]542 irq_spinlock_lock(&irq->lock, true);
543
[cecb0789]544 if (irq->notif_cfg.answerbox) {
[da1bafb]545 call_t *call = ipc_call_alloc(FRAME_ATOMIC);
[cecb0789]546 if (!call) {
[da1bafb]547 irq_spinlock_unlock(&irq->lock, true);
[cecb0789]548 return;
[b14e35f2]549 }
[da1bafb]550
[cecb0789]551 call->flags |= IPC_CALL_NOTIF;
552 /* Put a counter to the message */
553 call->priv = ++irq->notif_cfg.counter;
[da1bafb]554
[228e490]555 IPC_SET_IMETHOD(call->data, irq->notif_cfg.imethod);
[cecb0789]556 IPC_SET_ARG1(call->data, a1);
557 IPC_SET_ARG2(call->data, a2);
558 IPC_SET_ARG3(call->data, a3);
559 IPC_SET_ARG4(call->data, a4);
560 IPC_SET_ARG5(call->data, a5);
561
562 send_call(irq, call);
[b14e35f2]563 }
[da1bafb]564
565 irq_spinlock_unlock(&irq->lock, true);
[162f919]566}
[b45c443]567
[cc73a8a1]568/** @}
[b45c443]569 */
Note: See TracBrowser for help on using the repository browser.