blob: 080f2f9ee749b0eb195abf9251b59e21a03d1116 [file] [log] [blame]
/* Copyright (c) 2012 The Regents of the University of California
* Barret Rhoden <brho@cs.berkeley.edu>
* See LICENSE for details.
*
* Console (Keyboard/serial/whatever) related functions. */
#include <console.h>
#include <ros/ring_buffer.h>
#include <monitor.h>
#include <stdio.h>
struct kb_buffer cons_buf;
void kb_buf_init(struct kb_buffer *kb)
{
kb->prod_idx = 0;
kb->cons_idx = 0;
spinlock_init(&kb->buf_lock);
sem_init(&kb->buf_sem, 0);
/* no need to memset the buffer - we only read something that is written */
}
/* Producers don't block - their input is dropped. Should be rare for now; if
* it happens, it's probably a bug. */
void kb_add_to_buf(struct kb_buffer *kb, char c)
{
/* make sure we're a power of 2 */
static_assert(KB_BUF_SIZE == __RD32(KB_BUF_SIZE));
bool was_empty = FALSE;
spin_lock(&kb->buf_lock);
if (!__ring_full(KB_BUF_SIZE, kb->prod_idx, kb->cons_idx)) {
if (__ring_empty(kb->prod_idx, kb->cons_idx))
was_empty = TRUE;
kb->buf[kb->prod_idx % KB_BUF_SIZE] = c; // compiler should shift
kb->prod_idx++;
} else {
/* else drop the char */
printk("[kernel] KB buffer overflowed %c\n", c);
}
spin_unlock(&kb->buf_lock);
/* up the sem when it goes from empty->non_empty. rule for syncing with
* blockers: if there are any items in the buffer, either the sem is upped,
* or there is an active consumer. consumers immediately down (to become an
* active consumer). */
if (was_empty)
sem_up(&kb->buf_sem);
/* also note that multiple readers on the console/serial are going to fight
* for input and it is going to get interleaved - broader issue */
}
/* Will read cnt chars from the KB buf into dst. Will block until complete. */
void kb_get_from_buf(struct kb_buffer *kb, char *dst, size_t cnt)
{
unsigned int dst_idx = 0; /* aka, amt copied so far */
bool need_an_up = FALSE;
/* so long as we still need items, we'll sleep til there is activity, then
* grab everything we can til the kb buf is empty (or we're done). If we
* didn't empty the buf, we'll need to up the sem later. */
while (dst_idx < cnt) {
/* this will return immediately if some data is already there, o/w we
* block til there is some activity */
sem_down(&kb->buf_sem);
spin_lock(&kb->buf_lock);
/* under the current scheme, we should only have one active consumer at
* a time, so if we woke up, the ring must not be empty. */
assert(!__ring_empty(kb->prod_idx, kb->cons_idx));
while (!__ring_empty(kb->prod_idx, kb->cons_idx)) {
if (dst_idx == cnt) {
/* we're done, and it's not empty yet */
need_an_up = TRUE;
break;
}
dst[dst_idx] = kb->buf[kb->cons_idx % KB_BUF_SIZE];
kb->cons_idx++;
dst_idx++;
}
spin_unlock(&kb->buf_lock);
}
/* Remember: if the buf is non empty, there is either an active consumer or
* the sem is upped. */
if (need_an_up)
sem_up(&kb->buf_sem);
}
/* Kernel messages associated with the console. Arch-specific interrupt
* handlers need to call these. For add char, a0 = &cons_buf and a1 = the char
* you read. Call __run_mon on your 'magic' input. */
void __cons_add_char(uint32_t srcid, long a0, long a1, long a2)
{
kb_add_to_buf((struct kb_buffer*)a0, (char)a1);
}
void __run_mon(uint32_t srcid, long a0, long a1, long a2)
{
monitor(0);
}