blob: 963749cceb2488c973a76891bd35ebd22d92555e [file] [log] [blame]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
/*- import 'helpers/error.c' as error with context -*/
#include <assert.h>
#include <camkes/error.h>
#include <sel4/sel4.h>
#include <stddef.h>
#include <stdint.h>
#include <utils/util.h>
/*? macros.show_includes(me.instance.type.includes) ?*/
/*- set instance = me.instance.name -*/
/*- set interface = me.interface.name -*/
/*- set attr = "%s_attributes" % me.parent.from_interface.name -*/
/*- set irq= [] -*/
/*- set notification_obj = alloc_obj('notification', seL4_NotificationObject) -*/
/*- set notification = alloc_cap('notification', notification_obj, read=True) -*/
/*- set _irq = configuration[me.parent.from_instance.name].get(attr) -*/
/*- if _irq is not none -*/
/*- set attr_irq, attr_level, attr_trig = _irq.strip('"').split(',') -*/
/*- set irq_handler = alloc('irq', seL4_IRQHandler, number=int(attr_irq, 0), notification=my_cnode[notification]) -*/
/*- do irq.append((irq_handler, int(attr_level, 0), int(attr_trig, 0))) -*/
/*- endif -*/
/*- set lock = alloc('lock', seL4_NotificationObject, read=True, write=True) -*/
/* Interface-specific error handling */
/*- set error_handler = '%s_error_handler' % me.interface.name -*/
/*? error.make_error_handler(interface, error_handler) ?*/
#define MAX_CALLBACKS 10
static void (*volatile callbacks[MAX_CALLBACKS])(void*);
static void *callback_args[MAX_CALLBACKS];
static volatile int event_pending;
static volatile int sleepers;
#define CAS __sync_val_compare_and_swap
#define ATOMIC_INCREMENT(ptr) __sync_fetch_and_add((ptr), 1)
#define ATOMIC_DECREMENT(ptr) __sync_fetch_and_sub((ptr), 1)
#define SLEEP() \
do { \
ATOMIC_INCREMENT(&sleepers); \
assert(sleepers > 0); \
seL4_Wait(/*? lock ?*/, NULL); \
assert(sleepers > 0); \
ATOMIC_DECREMENT(&sleepers); \
} while (0)
#define WAKE() seL4_Signal(/*? lock ?*/)
int /*? me.interface.name ?*/__run(void) {
/* Set trigger mode */
seL4_IRQHandler_SetMode(/*? irq[0][0] ?*/, /*? irq[0][1] ?*/, /*? irq[0][2] ?*/);
while (1) {
int handled = 0;
seL4_Wait(/*? notification ?*/, NULL);
/* First preference: callbacks. */
if (!handled) {
for (int i = 0; i < MAX_CALLBACKS; ++i) {
void (*callback)(void*) = callbacks[i];
if (callback != NULL) {
callbacks[i] = NULL; /* No need for CAS. */
callback(callback_args[i]);
handled = 1;
}
}
}
/* There may in fact already be a pending event, but we don't care. */
event_pending = 1;
/* Second preference: waiters. */
if (!handled) {
if (sleepers > 0) { /* No lock required. */
WAKE();
/* Assume one of them will grab it. */
handled = 1;
}
}
/* Else, leave it for polling. */
}
UNREACHABLE();
}
int /*? me.interface.name ?*/_poll(void) {
return CAS(&event_pending, 1, 0);
}
void /*? me.interface.name ?*/_wait(void) {
while (!/*? me.interface.name ?*/_poll()) {
SLEEP();
}
}
int /*? me.interface.name ?*/_reg_callback(void (*callback)(void*), void *arg) {
int error;
for (int i = 0; i < MAX_CALLBACKS; ++i) {
if (CAS(&callbacks[i], NULL, callback) == NULL) {
callback_args[i] = arg;
error = seL4_IRQHandler_Ack(/*? irq[0][0] ?*/);
ERR_IF(error != 0, /*? error_handler ?*/, ((camkes_error_t){
.type = CE_SYSCALL_FAILED,
.instance = "/*? instance ?*/",
.interface = "/*? interface ?*/",
.description = "failed to acknowledge IRQ",
.syscall = IRQAckIRQ,
.error = error,
}), ({
return -1;
}));
return 0;
}
}
/* We didn't find an empty slot. */
return -1;
}