| /* 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); |
| } |