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