|
|
@@ -0,0 +1,103 @@
|
|
|
+#include "ioqueue.h"
|
|
|
+#include "../kernel/debug.h"
|
|
|
+#include "../kernel/interrupt.h"
|
|
|
+
|
|
|
+/// @brief 返回 pos 在缓冲区的下一个位置
|
|
|
+static int32_t next_pos(int32_t pos);
|
|
|
+
|
|
|
+/// @brief 初始化 ioqueue 队列
|
|
|
+void ioqueue_init(struct ioqueue *ioq)
|
|
|
+{
|
|
|
+ lock_init(&ioq->lock);
|
|
|
+ ioq->producer = NULL;
|
|
|
+ ioq->consumer = NULL;
|
|
|
+ ioq->head = 0;
|
|
|
+ ioq->tail = 0;
|
|
|
+}
|
|
|
+/// @brief 返回 pos 在缓冲区的下一个位置
|
|
|
+static int32_t next_pos(int32_t pos)
|
|
|
+{
|
|
|
+ return (pos + 1) % BUF_SIZE;
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief 判断队列是否已满
|
|
|
+/// @param ioq
|
|
|
+/// @return TRUE OR FALSE
|
|
|
+bool ioq_full(struct ioqueue *ioq)
|
|
|
+{
|
|
|
+ ASSERT(intr_get_status() == INTR_OFF);
|
|
|
+ return next_pos(ioq->head) == ioq->tail;
|
|
|
+}
|
|
|
+/// @brief 判断队列是否为空
|
|
|
+/// @param ioq
|
|
|
+/// @return TRUE OR FALSE
|
|
|
+bool ioq_empty(struct ioqueue *ioq)
|
|
|
+{
|
|
|
+ ASSERT(intr_get_status() == INTR_OFF);
|
|
|
+ return ioq->head == ioq->tail;
|
|
|
+}
|
|
|
+/// @brief 使生产者或消费者在此缓冲区上等待
|
|
|
+/// @param waiter
|
|
|
+static void ioq_wait(struct task_struct **waiter)
|
|
|
+{
|
|
|
+ ASSERT(*waiter == NULL && waiter != NULL);
|
|
|
+ *waiter = running_thread();
|
|
|
+ thread_block(TASK_BLOCKED); // 阻塞自己
|
|
|
+}
|
|
|
+/// @brief 唤醒 waiter
|
|
|
+static void wakeup(struct task_struct **waiter)
|
|
|
+{
|
|
|
+ ASSERT(waiter != NULL);
|
|
|
+ thread_unblock(*waiter);
|
|
|
+ *waiter = NULL;
|
|
|
+}
|
|
|
+/// @brief 消费者从 ioq 队列中获取一个字符
|
|
|
+char ioq_getchar(struct ioqueue *ioq)
|
|
|
+{
|
|
|
+ ASSERT(intr_get_status() == INTR_OFF);
|
|
|
+ // 如果 ioq 为空, 使消费者在此缓冲区上等待
|
|
|
+ // 把 ioq->consumer 记为当前线程自己,
|
|
|
+ // 目的是让生产者生产后唤醒
|
|
|
+ while (ioq_empty(ioq))
|
|
|
+ {
|
|
|
+ lock_acquire(&ioq->lock);
|
|
|
+ ioq_wait(&ioq->consumer);
|
|
|
+ lock_release(&ioq->lock);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取字符并从 ioq 队列中删除
|
|
|
+ char ret = ioq->buf[ioq->tail];
|
|
|
+ ioq->tail = next_pos(ioq->tail);
|
|
|
+
|
|
|
+ // 唤醒生产者
|
|
|
+ if (ioq->producer != NULL)
|
|
|
+ {
|
|
|
+ wakeup(&ioq->producer);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+/// @brief 生产者往 ioq 队列中添加一个字符
|
|
|
+void ioq_putchar(struct ioqueue *ioq, char byte)
|
|
|
+{
|
|
|
+ ASSERT(intr_get_status() == INTR_OFF);
|
|
|
+ // 如果 ioq 已满, 使生产者在此缓冲区上等待
|
|
|
+ // 把 ioq->producer 记为当前线程自己,
|
|
|
+ // 目的是让消费者消费后唤醒
|
|
|
+ while (ioq_full(ioq))
|
|
|
+ {
|
|
|
+ lock_acquire(&ioq->lock);
|
|
|
+ ioq_wait(&ioq->producer);
|
|
|
+ lock_release(&ioq->lock);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 往 ioq 队列中添加字符并使消费者在此缓冲区上等待
|
|
|
+ ioq->buf[ioq->head] = byte;
|
|
|
+ ioq->head = next_pos(ioq->head);
|
|
|
+
|
|
|
+ // 唤醒消费者
|
|
|
+ if (ioq->consumer != NULL)
|
|
|
+ {
|
|
|
+ wakeup(&ioq->consumer);
|
|
|
+ }
|
|
|
+}
|