Keyboard/char input buffering and irq handling
[akaros.git] / kern / src / console.c
1 /* Copyright (c) 2012 The Regents of the University of California
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * Console (Keyboard/serial/whatever) related functions. */
6
7 #include <console.h>
8 #include <ros/ring_buffer.h>
9 #include <monitor.h>
10 #include <stdio.h>
11
12 struct kb_buffer cons_buf;
13
14 void kb_buf_init(struct kb_buffer *kb)
15 {
16         kb->prod_idx = 0;
17         kb->cons_idx = 0;
18         spinlock_init(&kb->buf_lock);
19         init_sem(&kb->buf_sem, 0);
20         /* no need to memset the buffer - we only read something that is written */
21 }
22
23 /* Producers don't block - their input is dropped.  Should be rare for now; if
24  * it happens, it's probably a bug. */
25 void kb_add_to_buf(struct kb_buffer *kb, char c)
26 {
27         /* make sure we're a power of 2 */
28         static_assert(KB_BUF_SIZE == __RD32(KB_BUF_SIZE));
29         struct kthread *sleeper;
30         bool was_empty = FALSE;
31         spin_lock(&kb->buf_lock);
32         if (!__ring_full(KB_BUF_SIZE, kb->prod_idx, kb->cons_idx)) {
33                 if (__ring_empty(kb->prod_idx, kb->cons_idx))
34                         was_empty = TRUE;
35                 kb->buf[kb->prod_idx % KB_BUF_SIZE] = c;        // compiler should shift
36                 kb->prod_idx++;
37         } else {
38                 /* else drop the char */
39                 printk("[kernel] KB buffer overflowed %c\n", c);
40         }
41         spin_unlock(&kb->buf_lock);
42         /* up the sem when it goes from empty->non_empty.   rule for syncing with
43          * blockers: if there are any items in the buffer, either the sem is upped,
44          * or there is an active consumer.  consumers immediately down (to become an
45          * active consumer). */
46         if (was_empty) {
47                 sleeper = __up_sem(&kb->buf_sem, FALSE);
48                 if (sleeper)
49                         kthread_runnable(sleeper);
50         }
51         /* also note that multiple readers on the console/serial are going to fight
52          * for input and it is going to get interleaved - broader issue */
53 }
54
55 /* Will read cnt chars from the KB buf into dst.  Will block until complete. */
56 void kb_get_from_buf(struct kb_buffer *kb, char *dst, size_t cnt)
57 {
58         struct kthread *sleeper;
59         unsigned int dst_idx = 0; /* aka, amt copied so far */
60         bool need_an_up = FALSE;
61
62         /* so long as we still need items, we'll sleep til there is activity, then
63          * grab everything we can til the kb buf is empty (or we're done).  If we
64          * didn't empty the buf, we'll need to up the sem later. */
65         while (dst_idx < cnt) {
66                 /* this will return immediately if some data is already there, o/w we
67                  * block til there is some activity */
68                 sleep_on(&kb->buf_sem);
69                 spin_lock(&kb->buf_lock);
70                 /* under the current scheme, we should only have one active consumer at
71                  * a time, so if we woke up, the ring must not be empty. */
72                 assert(!__ring_empty(kb->prod_idx, kb->cons_idx));
73                 while (!__ring_empty(kb->prod_idx, kb->cons_idx)) {
74                         if (dst_idx == cnt) {
75                                 /* we're done, and it's not empty yet */
76                                 need_an_up = TRUE;
77                                 break;
78                         }
79                         dst[dst_idx] = kb->buf[kb->cons_idx % KB_BUF_SIZE];
80                         kb->cons_idx++;
81                         dst_idx++;
82                 }
83                 spin_unlock(&kb->buf_lock);
84         }
85         /* Remember: if the buf is non empty, there is either an active consumer or
86          * the sem is upped. */
87         if (need_an_up) {
88                 sleeper = __up_sem(&kb->buf_sem, FALSE);
89                 if (sleeper)
90                         kthread_runnable(sleeper);
91         }
92 }
93
94 /* Kernel messages associated with the console.  Arch-specific interrupt
95  * handlers need to call these.  For add char, a0 = &cons_buf and a1 = the char
96  * you read.  Call __run_mon on your 'magic' input.  */
97 void __cons_add_char(struct trapframe *tf, uint32_t srcid, long a0, long a1,
98                      long a2)
99 {
100         kb_add_to_buf((struct kb_buffer*)a0, (char)a1);
101 }
102
103 void __run_mon(struct trapframe *tf, uint32_t srcid, long a0, long a1,
104                long a2)
105 {
106         monitor(0);
107 }