From 328086939f0f152f9cfc140530d44e4a716685b6 Mon Sep 17 00:00:00 2001
From: Rahix <rahix@rahix.de>
Date: Sat, 3 Aug 2019 12:28:27 +0200
Subject: [PATCH] fix(serial): Fix UART receive call violating API rules

Signed-off-by: Rahix <rahix@rahix.de>
---
 epicardium/epicardium.h     | 51 +++++++++++++++++++++++++++++--------
 epicardium/modules/serial.c | 23 ++++++++++-------
 pycardium/main.c            |  7 ++---
 pycardium/mphalport.c       | 21 +++++++++++++--
 pycardium/mphalport.h       |  3 +++
 5 files changed, 81 insertions(+), 24 deletions(-)

diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h
index 7b486428..a9b17b49 100644
--- a/epicardium/epicardium.h
+++ b/epicardium/epicardium.h
@@ -29,8 +29,9 @@ typedef unsigned int size_t;
 /* clang-format off */
 #define API_SYSTEM_EXIT             0x1 /* TODO */
 #define API_SYSTEM_EXEC             0x2 /* TODO */
-#define API_UART_WRITE              0x3
-#define API_UART_READ               0x4
+#define API_UART_WRITE_STR          0x3
+#define API_UART_READ_CHAR          0x4
+#define API_UART_READ_STR           0x5 /* TODO */
 #define API_STREAM_READ             0x6
 #define API_INTERRUPT_ENABLE        0x7
 #define API_INTERRUPT_DISABLE       0x8
@@ -102,7 +103,7 @@ API(API_INTERRUPT_DISABLE, int epic_interrupt_disable(api_int_id_t int_id));
 #define EPIC_INT_RESET                  0
 /** ``^C`` interrupt. See :c:func:`epic_isr_ctrl_c` for details.  */
 #define EPIC_INT_CTRL_C                 1
-/** TODO */
+/** UART Receive interrupt.  See :c:func:`epic_isr_uart_rx`. */
 #define EPIC_INT_UART_RX                2
 /** RTC Alarm interrupt.  See :c:func:`epic_isr_rtc_alarm` */
 #define EPIC_INT_RTC_ALARM              3
@@ -129,19 +130,49 @@ API_ISR(EPIC_INT_RESET, epic_isr_reset);
  * :param str:  String to write.  Does not necessarily have to be NULL-terminated.
  * :param length:  Amount of bytes to print.
  */
-API(API_UART_WRITE, void epic_uart_write_str(const char *str, intptr_t length));
+API(API_UART_WRITE_STR, void epic_uart_write_str(
+	const char *str,
+	intptr_t length
+));
 
 /**
- * Blocking read a single character from any connected serial device.
- * ``epic_uart_read_chr`` only returns once one byte has been read.
  *
- * .. todo::
+ * Try reading a single character from any connected serial device.  If nothing
+ * is available, :c:func:`epic_uart_read_char` returns ``(-1)``.
  *
- *    This API function is currently in violation of the API rules.
+ * :return:  The byte or ``(-1)`` if no byte was available.
+ */
+API(API_UART_READ_CHAR, int epic_uart_read_char(void));
+
+/**
+ * **Interrupt Service Routine**
+ *
+ * UART receive interrupt.  This interrupt is triggered whenever a new character
+ * becomes available on any connected UART device.  This function is weakly
+ * aliased to :c:func:`epic_isr_default` by default.
+ *
+ * **Example**:
  *
- * :return:  The byte.
+ * .. code-block:: cpp
+ *
+ *    void epic_isr_uart_rx(void)
+ *    {
+ *            char buffer[33];
+ *            int n = epic_uart_read_str(&buffer, sizeof(buffer) - 1);
+ *            buffer[n] = '\0';
+ *            printf("Got: %s\n", buffer);
+ *    }
+ *
+ *    int main(void)
+ *    {
+ *            epic_interrupt_enable(EPIC_INT_UART_RX);
+ *
+ *            while (1) {
+ *                    __WFI();
+ *            }
+ *    }
  */
-API(API_UART_READ, char epic_uart_read_chr(void));
+API_ISR(EPIC_INT_UART_RX, epic_isr_uart_rx);
 
 /**
  * **Interrupt Service Routine**
diff --git a/epicardium/modules/serial.c b/epicardium/modules/serial.c
index 71df9e1e..ac573757 100644
--- a/epicardium/modules/serial.c
+++ b/epicardium/modules/serial.c
@@ -1,5 +1,7 @@
-#include <stdint.h>
-#include <stdio.h>
+#include "epicardium.h"
+#include "api/interrupt-sender.h"
+#include "modules/log.h"
+#include "modules/modules.h"
 
 #include "max32665.h"
 #include "cdcacm.h"
@@ -9,9 +11,8 @@
 #include "task.h"
 #include "queue.h"
 
-#include "modules.h"
-#include "modules/log.h"
-#include "api/interrupt-sender.h"
+#include <stdint.h>
+#include <stdio.h>
 
 /* Task ID for the serial handler */
 TaskHandle_t serial_task_id = NULL;
@@ -31,13 +32,15 @@ void epic_uart_write_str(const char *str, intptr_t length)
 }
 
 /*
- * Blocking API-call to read a character from the queue.
+ * API-call to read a character from the queue.
  */
-char epic_uart_read_chr(void)
+int epic_uart_read_char(void)
 {
 	char chr;
-	xQueueReceive(read_queue, &chr, portMAX_DELAY);
-	return chr;
+	if (xQueueReceive(read_queue, &chr, 0) == pdTRUE) {
+		return (int)chr;
+	}
+	return (-1);
 }
 
 /* Interrupt handler needed for SDK UART implementation */
@@ -64,6 +67,8 @@ static void enqueue_char(char chr)
 		/* Queue overran, wait a bit */
 		vTaskDelay(portTICK_PERIOD_MS * 50);
 	}
+
+	api_interrupt_trigger(EPIC_INT_UART_RX);
 }
 
 void vSerialTask(void *pvParameters)
diff --git a/pycardium/main.c b/pycardium/main.c
index 273e85eb..4d708727 100644
--- a/pycardium/main.c
+++ b/pycardium/main.c
@@ -1,3 +1,5 @@
+#include "mphalport.h"
+
 #include "max32665.h"
 
 #include "lib/utils/pyexec.h"
@@ -13,12 +15,11 @@ extern void *__HeapBase, *__HeapLimit;
 
 int main(void)
 {
+	pycardium_hal_init();
+
 	mp_stack_set_top(&__StackTop);
 	mp_stack_set_limit((mp_int_t)&__StackLimit);
 
-	/* TMR5 is used to notify on keyboard interrupt */
-	NVIC_EnableIRQ(TMR5_IRQn);
-
 	while (1) {
 		gc_init(&__HeapBase + 1024 * 10, &__HeapLimit);
 
diff --git a/pycardium/mphalport.c b/pycardium/mphalport.c
index b97c1dd4..1fa75b64 100644
--- a/pycardium/mphalport.c
+++ b/pycardium/mphalport.c
@@ -1,5 +1,5 @@
-#include "api/common.h"
 #include "epicardium.h"
+#include "api/common.h"
 
 #include "max32665.h"
 #include "mxc_delay.h"
@@ -20,6 +20,19 @@
 #include <stdio.h>
 #include <string.h>
 
+/* Initialize everything for MicroPython */
+void pycardium_hal_init(void)
+{
+	/* TMR5 is used for interrupts from Epicardium */
+	NVIC_EnableIRQ(TMR5_IRQn);
+
+	/*
+	 * Enable UART RX Interrupt so Pycardium can sleep until
+	 * a character becomes available.
+	 */
+	epic_interrupt_enable(EPIC_INT_UART_RX);
+}
+
 /******************************************************************************
  * Serial Communication
  */
@@ -27,7 +40,11 @@
 /* Receive single character */
 int mp_hal_stdin_rx_chr(void)
 {
-	return (int)epic_uart_read_chr();
+	int chr;
+	while ((chr = epic_uart_read_char()) < 0) {
+		__WFI();
+	}
+	return chr;
 }
 
 /* Send a string */
diff --git a/pycardium/mphalport.h b/pycardium/mphalport.h
index ad34087f..5827a483 100644
--- a/pycardium/mphalport.h
+++ b/pycardium/mphalport.h
@@ -1,3 +1,6 @@
 #include "py/mpconfig.h"
 
 void mp_hal_set_interrupt_char(char c);
+
+/* Init everything */
+void pycardium_hal_init(void);
-- 
GitLab