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 <stdint.h> 16#include <stdbool.h> 17#include <assert.h> 18#include <autoconf.h> 19 20#include "device_input.h" 21#include "state.h" 22 23#include <refos-util/serv_connect.h> 24#include <refos-rpc/data_server.h> 25 26/*! @file 27 @brief Console Server input device implementation. 28 29 Most of the actual input drivers work is fortunately done by the libplatsupport library. This 30 module simply provides a client waiting list layer on top of getchar, managing client getc() and 31 read() syscalls into stdin. 32 33 When a character is typed but there is no one to listen, it goes into the backlog, buffered for 34 the next read() or getc() call. If the backlog is full, the oldest character is lost. 35 36 When a client wants to getc() with blocking enabled and there isn't a character already waiting 37 to be recieved, the Console server must block the calling client until an RX irq comes in from the 38 input device. This is implemented using seL4_SaveCaller functionality, saving the reply endpoint 39 capability into a 'waiting list'. Whenever we recieve an IRQ, we go through this waiting list 40 and reply to any waiters, unblocking their syscall. 41*/ 42 43/*! @brief Add a new character onto the getchar queue. 44 @param s The input state structure (struct input_state*) 45 @param c The new character to push. 46*/ 47static void 48input_push_char(struct input_state *s, int c) 49{ 50 /* If backlog is too big, prune it. */ 51 while (cqueue_size(&s->inputBacklog) >= CONSERV_DEVICE_INPUT_BACKLOG_MAXSIZE) { 52 cqueue_item_t item = cqueue_pop(&s->inputBacklog); 53 (void) item; 54 } 55 56 /* Push new character onto the queue. */ 57 bool success = cqueue_push(&s->inputBacklog, (cqueue_item_t) c); 58 if (!success) { 59 ROS_ERROR("input backlog queue should not be full. conserv book-keeping error."); 60 } 61} 62 63/*! @brief The IRQ handling callback function. 64 65 This callback function gets called from the interrupt dispatcher module to handle RX irqs. 66 It adds the inputted character to the backlog, and then goes through the waiting list and 67 replies to any waiters. 68 69 @param cookie The input state structure (struct input_state*) 70 @param irq The IRQ number. 71*/ 72static void 73input_handle_irq(void *cookie, uint32_t irq) 74{ 75 struct input_state *s = (struct input_state *) cookie; 76 assert(s && s->magic == CONSERV_DEVICE_INPUT_MAGIC); 77 ps_cdev_handle_irq(&conServ.devSerial, irq); 78 79 while (1) { 80 int c = ps_cdev_getchar(&conServ.devSerial); 81 if (c == -1) { 82 break; 83 } 84 dvprintf("You typed [%c]\n", c); 85 input_push_char(s, c); 86 } 87 88 #ifdef PLAT_PC99 89 while (conServ.keyboardEnabled) { 90 int c = ps_cdev_getchar(&conServ.devKeyboard); 91 if (c == -1) { 92 break; 93 } 94 dvprintf("You typed on keyboard [%c]\n", c); 95 input_push_char(s, c); 96 } 97 #endif 98 99 /* Notify any waiters. */ 100 for (int i = 0; i < cvector_count(&s->waiterList); i++) { 101 struct input_waiter *waiter = (struct input_waiter*) cvector_get(&s->waiterList, i); 102 assert(waiter && waiter->magic == CONSERV_DEVICE_INPUT_WAITER_MAGIC); 103 assert(waiter->reply && waiter->client); 104 105 if (cqueue_size(&s->inputBacklog) <= 0) { 106 /* No more backlog to reply to. Cannot reply to more waiters. */ 107 break; 108 } 109 110 waiter->client->rpcClient.skip_reply = false; 111 waiter->client->rpcClient.reply = waiter->reply; 112 113 /* Reply to the waiter. */ 114 if (waiter->type == INPUT_WAITERTYPE_GETC) { 115 int ch = (int) cqueue_pop(&s->inputBacklog); 116 reply_data_getc((void*) waiter->client, ch); 117 } else { 118 assert(!"Not implemented."); 119 } 120 121 /* Delete the saved reply cap, and free the structure. */ 122 waiter->client->rpcClient.reply = 0; 123 csfree_delete(waiter->reply); 124 waiter->magic = 0x0; 125 free(waiter); 126 cvector_set(&s->waiterList, i, (cvector_item_t) NULL); 127 cvector_delete(&s->waiterList, i); 128 i--; 129 } 130} 131 132void 133input_init(struct input_state *s) 134{ 135 assert(s); 136 s->magic = CONSERV_DEVICE_INPUT_MAGIC; 137 138 /* Initialise the input backlog and waiting list. */ 139 cqueue_init(&s->inputBacklog, CONSERV_DEVICE_INPUT_BACKLOG_MAXSIZE); 140 cvector_init(&s->waiterList); 141 142 /* Loop through every possible IRQ, and get the ones that the input device needs to 143 listen to. */ 144 for (uint32_t i = 0; i < DEVICE_MAX_IRQ; i++) { 145 if (ps_cdev_produces_irq(&conServ.devSerial, i)) { 146 dev_handle_irq(&conServ.irqState, i, input_handle_irq, (void*) s); 147 input_handle_irq((void*) s, i); 148 } 149 #ifdef PLAT_PC99 150 if (conServ.keyboardEnabled) { 151 if (ps_cdev_produces_irq(&conServ.devKeyboard, i)) { 152 dev_handle_irq(&conServ.irqState, i, input_handle_irq, (void*) s); 153 input_handle_irq((void*) s, i); 154 } 155 } 156 #endif 157 } 158} 159 160int 161input_read(struct input_state *s, int *dest, uint32_t count) 162{ 163 assert(s && s->magic == CONSERV_DEVICE_INPUT_MAGIC); 164 if (!dest || count == 0) { 165 return 0; 166 } 167 168 if (cqueue_size(&s->inputBacklog) == 0) { 169 /* Nothing in the backlog. We're going to have to block. */ 170 return 0; 171 } 172 173 /* Read in from backlog. */ 174 int i = 0; 175 while (cqueue_size(&s->inputBacklog) > 0) { 176 int c = (int) cqueue_pop(&s->inputBacklog); 177 dest[i++] = c; 178 if (i >= count) { 179 break; 180 } 181 } 182 183 return i; 184} 185 186int 187input_save_caller_as_waiter(struct input_state *s, struct srv_client *c, bool type) 188{ 189 assert(s && s->magic == CONSERV_DEVICE_INPUT_MAGIC); 190 assert(c && c->magic == CONSERV_CLIENT_MAGIC); 191 int error = EINVALID; 192 193 /* Allocate and fill out waiter structure. */ 194 struct input_waiter *waiter = malloc(sizeof(struct input_waiter)); 195 if (!waiter) { 196 ROS_ERROR("input_save_caller_as_waiter failed to alloc waiter struct."); 197 return ENOMEM; 198 } 199 waiter->magic = CONSERV_DEVICE_INPUT_WAITER_MAGIC; 200 waiter->client = c; 201 waiter->type = type; 202 203 /* Allocate a cslot to save the reply cap into. */ 204 waiter->reply = csalloc(); 205 if (!waiter->reply) { 206 ROS_ERROR("input_save_caller_as_waiter failed to alloc cslot."); 207 error = ENOMEM; 208 goto exit1; 209 } 210 211 /* Save current caller into the reply cap. */ 212 error = seL4_CNode_SaveCaller(REFOS_CSPACE, waiter->reply, REFOS_CDEPTH); 213 if (error != seL4_NoError) { 214 ROS_ERROR("input_save_caller_as_waiter failed to save caller."); 215 error = EINVALID; 216 goto exit2; 217 } 218 219 /* Add to waiter list. (Takes ownership) */ 220 cvector_add(&s->waiterList, (cvector_item_t) waiter); 221 222 return ESUCCESS; 223 224 /* Exit stack. */ 225exit2: 226 csfree(waiter->reply); 227exit1: 228 waiter->magic = 0; 229 free(waiter); 230 return error; 231} 232 233void 234input_purge_client(struct srv_client *client) 235{ 236 assert(!"not implemented."); 237} 238