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