1/*
2 * Copyright 2016, 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(D61_BSD)
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <assert.h>
17#include <refos-util/device_irq.h>
18#include <refos-util/dprintf.h>
19#include <refos-util/serv_common.h>
20
21/*! @file
22    @brief Device IRQ handling library. */
23
24void dev_irq_init(dev_irq_state_t *irqState, dev_irq_config_t config)
25{
26    assert(irqState);
27    memset(irqState, 0, sizeof(dev_irq_state_t));
28    dprintf("Initialising IRQ handler state...\n");
29
30    if (config.numIRQChannels <= 0 || config.numIRQChannels >= DEVICE_IRQ_BADGE_MAX_CHANNELS) {
31        ROS_ERROR("config.numIRQChannels is invalid.\n");
32        ROS_ERROR("IRQ handler init failed.\n");
33        return;
34    }
35    if (!config.getIRQHandlerEndpoint) {
36        ROS_ERROR("Please provide a callback function for minting a IRQ handler cap.\n");
37        ROS_ERROR("IRQ handler init failed.\n");
38        return;
39    }
40    if (!config.notifyAsyncEP) {
41        ROS_ERROR("Please provide an async endpoint to mint IRQ badges from.\n");
42        ROS_ERROR("IRQ handler init failed.\n");
43        return;
44    }
45
46    /* Initialise IRQ channel table. */
47    for (int i = 0; i < config.numIRQChannels; i++) {
48        cvector_init(&irqState->channel[i]);
49    }
50
51    irqState->cfg = config;
52    irqState->magic = DEVICE_IRQ_MAGIC;
53}
54
55int dev_handle_irq(dev_irq_state_t *irqState, uint32_t irq,
56                   dev_irq_callback_fn_t callback, void *cookie)
57{
58    assert(irqState && irqState->magic == DEVICE_IRQ_MAGIC);
59
60    if (irq >= DEVICE_MAX_IRQ) {
61        ROS_ERROR("dev_handle_irq IRQ num too high. Try raising DEVICE_MAX_IRQ.");
62        assert(!"Try raising DEVICE_MAX_IRQ.");
63        return EINVALIDPARAM;
64    }
65
66    /* Retrieve the handler, if necessary. */
67    if (!irqState->handler[irq].handler) {
68        assert(irqState->cfg.getIRQHandlerEndpoint);
69        irqState->handler[irq].handler = irqState->cfg.getIRQHandlerEndpoint(
70            irqState->cfg.getIRQHandlerEndpointCookie, irq
71        );
72        if (!irqState->handler[irq].handler) {
73            ROS_WARNING("dev_handle_irq : could not get IRQ handler for irq %u.\n", irq);
74            return EINVALID;
75        }
76    }
77
78    /* Determine next round-robin channel to go in. */
79    uint32_t nextChannel = irqState->cfg.badgeBaseBit + irqState->nextIRQChannel;
80    cvector_add(&irqState->channel[irqState->nextIRQChannel], (cvector_item_t) irq);
81    irqState->nextIRQChannel = (irqState->nextIRQChannel + 1) % irqState->cfg.numIRQChannels;
82
83    /* Mint the badged AEP. */
84    assert(irqState->cfg.notifyAsyncEP);
85    seL4_CPtr irqBadge = srv_mint((1 << nextChannel) | irqState->cfg.badgeMaskBits,
86                                  irqState->cfg.notifyAsyncEP);
87    if (!irqBadge) {
88        ROS_WARNING("dev_handle_irq : could not mint badged aep for irq %u.\n", irq);
89        return EINVALID;
90    }
91
92    /* Assign AEP to the IRQ handler. */
93    int error = seL4_IRQHandler_SetNotification(irqState->handler[irq].handler, irqBadge);
94    if (error) {
95        ROS_WARNING("dev_handle_irq : could not set notify aep for irq %u.\n", irq);
96        csfree_delete(irqBadge);
97        return EINVALID;
98    }
99    seL4_IRQHandler_Ack(irqState->handler[irq].handler);
100
101    /* Set callback function and cookie. */
102    irqState->handler[irq].callback = callback;
103    irqState->handler[irq].cookie = cookie;
104
105    csfree_delete(irqBadge);
106    return ESUCCESS;
107}
108
109int dev_dispatch_interrupt(dev_irq_state_t *irqState, srv_msg_t *m)
110{
111    assert(irqState && irqState->magic == DEVICE_IRQ_MAGIC);
112
113    if ((m->badge & irqState->cfg.badgeMaskBits) == 0) {
114        return DISPATCH_PASS;
115    }
116
117    /* Loop through every bit in the bitmask to determine which IRQs fired. */
118    bool IRQFound = false;
119    for (int i = 0; i <  irqState->cfg.numIRQChannels; i++) {
120        if (m->badge & (1 << (irqState->cfg.badgeBaseBit + i))) {
121            IRQFound = true;
122
123            /* Go through every IRQ clash. If the server needs to handle a lot of different IRQs
124               then this will lead to false positives. This seems to be a kernel API limitation
125               of a single threaded server design. More threads listening to multiple endpoints
126               will avoid this problem of IRQ clash.
127
128               Hopefully in practical environments, this won't be a problem since we probably won't
129               have a small system that needs a lot of different interrupts.
130            */
131            int nIRQs = cvector_count(&irqState->channel[i]);
132            for (int j = 0; j < nIRQs; j++) {
133                uint32_t irq = (uint32_t) cvector_get(&irqState->channel[i], j);
134                assert (irq < DEVICE_MAX_IRQ);
135
136                if (irqState->handler[irq].callback) {
137                    irqState->handler[irq].callback(irqState->handler[irq].cookie, irq);
138                }
139                if (irqState->handler[irq].handler) {
140                    seL4_IRQHandler_Ack(irqState->handler[irq].handler);
141                }
142            }
143
144        }
145    }
146
147    if (!IRQFound) {
148        return DISPATCH_PASS;
149    }
150
151    return DISPATCH_SUCCESS;
152}
153