1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13/*- import 'helpers/error.c' as error with context -*/
14
15#include <assert.h>
16#include <camkes/error.h>
17#include <sel4/sel4.h>
18#include <stddef.h>
19#include <stdint.h>
20#include <utils/util.h>
21
22/*? macros.show_includes(me.instance.type.includes) ?*/
23
24/*- set instance = me.instance.name -*/
25/*- set interface = me.interface.name -*/
26
27/*- set attr = "%s_attributes" % me.parent.from_interface.name -*/
28/*- set irq= [] -*/
29/*- set notification_obj = alloc_obj('notification', seL4_NotificationObject) -*/
30/*- set notification = alloc_cap('notification', notification_obj, read=True) -*/
31/*- set _irq = configuration[me.parent.from_instance.name].get(attr) -*/
32/*- if _irq is not none -*/
33    /*- set attr_irq, attr_level, attr_trig = _irq.strip('"').split(',') -*/
34    /*- set irq_handler = alloc('irq', seL4_IRQHandler, number=int(attr_irq, 0), notification=my_cnode[notification]) -*/
35    /*- do irq.append((irq_handler, int(attr_level, 0), int(attr_trig, 0))) -*/
36/*- endif -*/
37/*- set lock = alloc('lock', seL4_NotificationObject, read=True, write=True) -*/
38
39/* Interface-specific error handling */
40/*- set error_handler = '%s_error_handler' % me.interface.name -*/
41/*? error.make_error_handler(interface, error_handler) ?*/
42
43#define MAX_CALLBACKS 10
44
45static void (*volatile callbacks[MAX_CALLBACKS])(void*);
46static void *callback_args[MAX_CALLBACKS];
47static volatile int event_pending;
48static volatile int sleepers;
49
50#define CAS __sync_val_compare_and_swap
51#define ATOMIC_INCREMENT(ptr) __sync_fetch_and_add((ptr), 1)
52#define ATOMIC_DECREMENT(ptr) __sync_fetch_and_sub((ptr), 1)
53
54#define SLEEP() \
55    do { \
56        ATOMIC_INCREMENT(&sleepers); \
57        assert(sleepers > 0); \
58        seL4_Wait(/*? lock ?*/, NULL); \
59        assert(sleepers > 0); \
60        ATOMIC_DECREMENT(&sleepers); \
61    } while (0)
62
63#define WAKE() seL4_Signal(/*? lock ?*/)
64
65int /*? me.interface.name ?*/__run(void) {
66    /* Set trigger mode */
67    seL4_IRQHandler_SetMode(/*? irq[0][0] ?*/, /*? irq[0][1] ?*/, /*? irq[0][2] ?*/);
68    while (1) {
69        int handled = 0;
70
71        seL4_Wait(/*? notification ?*/, NULL);
72
73        /* First preference: callbacks. */
74        if (!handled) {
75            for (int i = 0; i < MAX_CALLBACKS; ++i) {
76                void (*callback)(void*) = callbacks[i];
77                if (callback != NULL) {
78                    callbacks[i] = NULL; /* No need for CAS. */
79                    callback(callback_args[i]);
80                    handled = 1;
81                }
82            }
83        }
84
85        /* There may in fact already be a pending event, but we don't care. */
86        event_pending = 1;
87
88        /* Second preference: waiters. */
89        if (!handled) {
90            if (sleepers > 0) { /* No lock required. */
91                WAKE();
92                /* Assume one of them will grab it. */
93                handled = 1;
94            }
95        }
96
97        /* Else, leave it for polling. */
98    }
99
100    UNREACHABLE();
101}
102
103int /*? me.interface.name ?*/_poll(void) {
104    return CAS(&event_pending, 1, 0);
105}
106
107void /*? me.interface.name ?*/_wait(void) {
108    while (!/*? me.interface.name ?*/_poll()) {
109        SLEEP();
110    }
111}
112
113int /*? me.interface.name ?*/_reg_callback(void (*callback)(void*), void *arg) {
114    int error;
115    for (int i = 0; i < MAX_CALLBACKS; ++i) {
116        if (CAS(&callbacks[i], NULL, callback) == NULL) {
117            callback_args[i] = arg;
118            error = seL4_IRQHandler_Ack(/*? irq[0][0] ?*/);
119            ERR_IF(error != 0, /*? error_handler ?*/, ((camkes_error_t){
120                    .type = CE_SYSCALL_FAILED,
121                    .instance = "/*? instance ?*/",
122                    .interface = "/*? interface ?*/",
123                    .description = "failed to acknowledge IRQ",
124                    .syscall = IRQAckIRQ,
125                    .error = error,
126                }), ({
127                    return -1;
128                }));
129            return 0;
130        }
131    }
132    /* We didn't find an empty slot. */
133    return -1;
134}
135