135 lines
3.0 KiB
C
135 lines
3.0 KiB
C
#include "sys/dev/ring.h"
|
|
#include "sys/thread.h"
|
|
#include "sys/assert.h"
|
|
#include "sys/sched.h"
|
|
#include "sys/debug.h"
|
|
#include "sys/heap.h"
|
|
|
|
int ring_readable(struct ring *ring) {
|
|
// | . . . . R X X X W . . . |
|
|
if (ring->rd <= ring->wr) {
|
|
return ring->wr - ring->rd;
|
|
// | X X W . . . . . . R X X |
|
|
} else {
|
|
return ring->wr + (ring->cap - ring->rd);
|
|
}
|
|
}
|
|
|
|
static int ring_writable(struct ring *ring) {
|
|
if (ring->wr == ring->cap - 1 && ring->rd == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// | X X X X R . . . W X X X |
|
|
if (ring->rd <= ring->wr) {
|
|
return ring->wr + (ring->cap - ring->rd);
|
|
// | . . W X X X X R . . . . |
|
|
} else {
|
|
if (ring->rd == ring->wr + 1) {
|
|
return 0;
|
|
}
|
|
return ring->rd - ring->wr;
|
|
}
|
|
}
|
|
|
|
static void ring_advance_read(struct ring *ring) {
|
|
if (ring->rd == ring->cap - 1) {
|
|
ring->rd = 0;
|
|
} else {
|
|
++ring->rd;
|
|
}
|
|
}
|
|
|
|
static void ring_advance_write(struct ring *ring) {
|
|
if (ring->wr == ring->cap - 1) {
|
|
ring->wr = 0;
|
|
} else {
|
|
++ring->wr;
|
|
}
|
|
}
|
|
|
|
int ring_getc(struct thread *ctx, struct ring *ring, char *c, int err) {
|
|
if (err) {
|
|
if (!ring_readable(ring)) {
|
|
return -1;
|
|
}
|
|
} else {
|
|
do {
|
|
if (ring->flags & (RING_SIGNAL_BRK | RING_SIGNAL_EOF)) {
|
|
ring->flags &= ~RING_SIGNAL_BRK;
|
|
// TODO: send SIGINT on break
|
|
return -1;
|
|
}
|
|
|
|
if (!ring_readable(ring)) {
|
|
asm volatile ("cli");
|
|
_assert(ctx);
|
|
|
|
ctx->wait_prev = NULL;
|
|
ctx->wait_next = ctx;
|
|
ring->reader_head = ctx;
|
|
|
|
sched_unqueue(ctx, THREAD_WAITING_IO);
|
|
} else {
|
|
break;
|
|
}
|
|
} while (1);
|
|
}
|
|
|
|
*c = ring->base[ring->rd];
|
|
ring_advance_read(ring);
|
|
return 0;
|
|
}
|
|
|
|
int ring_putc(struct thread *ctx, struct ring *ring, char c, int wait) {
|
|
if (wait) {
|
|
while (!ring_writable(ring)) {
|
|
panic("Not implemented\n");
|
|
}
|
|
}
|
|
|
|
ring->base[ring->wr] = c;
|
|
ring_advance_write(ring);
|
|
|
|
// Notify a single reader a character is available
|
|
if (ring->reader_head) {
|
|
struct thread *thr = ring->reader_head;
|
|
ring->reader_head = NULL; //thr->wait_next;
|
|
|
|
sched_queue(thr);
|
|
|
|
_assert(thr);
|
|
_assert(thr->next);
|
|
_assert(thr->prev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ring_write(struct thread *ctx, struct ring *ring, const void *buf, size_t len, int wait) {
|
|
for (size_t i = 0; i < len; ++i) {
|
|
if (ring_putc(ctx, ring, ((const char *) buf)[i], wait) != 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ring_signal(struct ring *r, int s) {
|
|
r->flags |= s;
|
|
}
|
|
|
|
int ring_init(struct ring *r, size_t cap) {
|
|
r->cap = cap;
|
|
r->rd = 0;
|
|
r->wr = 0;
|
|
r->flags = 0;
|
|
r->reader_head = NULL;
|
|
|
|
if (!(r->base = kmalloc(cap))) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|