serial.c 6.85 KB
Newer Older
1
2
3
4
#include "epicardium.h"
#include "api/interrupt-sender.h"
#include "modules/log.h"
#include "modules/modules.h"
Rahix's avatar
Rahix committed
5

Rahix's avatar
Rahix committed
6
#include "max32665.h"
swym's avatar
swym committed
7
#include "usb/cdcacm.h"
Rahix's avatar
Rahix committed
8
9
10
11
12
#include "uart.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
13
#include "stream_buffer.h"
Rahix's avatar
Rahix committed
14

15
16
#include <stdint.h>
#include <stdio.h>
17

18
19
20
/* The serial console in use (UART0) */
extern mxc_uart_regs_t *ConsoleUart;

Rahix's avatar
Rahix committed
21
/* Task ID for the serial handler */
Rahix's avatar
Rahix committed
22
TaskHandle_t serial_task_id = NULL;
Rahix's avatar
Rahix committed
23

Rahix's avatar
Rahix committed
24
/* Read queue, filled by both UART and CDCACM */
Rahix's avatar
Rahix committed
25
static QueueHandle_t read_queue;
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/* Stream Buffer for handling all writes to serial */
static StreamBufferHandle_t write_stream_buffer = NULL;

void serial_init()
{
	/* Setup read queue */
	static uint8_t buffer[sizeof(char) * SERIAL_READ_BUFFER_SIZE];
	static StaticQueue_t read_queue_data;
	read_queue = xQueueCreateStatic(
		SERIAL_READ_BUFFER_SIZE, sizeof(char), buffer, &read_queue_data
	);

	/* Setup write queue */
	static uint8_t ucWrite_stream_buffer[SERIAL_WRITE_STREAM_BUFFER_SIZE];
	static StaticStreamBuffer_t xStream_buffer_struct;
	write_stream_buffer = xStreamBufferCreateStatic(
		sizeof(ucWrite_stream_buffer),
		1,
		ucWrite_stream_buffer,
		&xStream_buffer_struct
	);
}
Rahix's avatar
Rahix committed
48

Rahix's avatar
Rahix committed
49
50
51
/*
 * API-call to write a string.  Output goes to both CDCACM and UART
 */
52
void epic_uart_write_str(const char *str, intptr_t length)
Rahix's avatar
Rahix committed
53
{
54
55
	if (length == 0) {
		return;
56
57
	}

58
59
60
61
62
63
64
65
	/*
	 * Check if the stream buffer is even initialized yet
	 */
	if (write_stream_buffer == NULL) {
		UART_Write(ConsoleUart, (uint8_t *)str, length);
		cdcacm_write((uint8_t *)str, length);
		return;
	}
66
67

	if (xPortIsInsideInterrupt()) {
68
69
70
71
72
73
74
75
76
77
78
79
80
81
		BaseType_t resched1 = pdFALSE;
		BaseType_t resched2 = pdFALSE;

		/*
		 * Enter a critial section so no other task can write to the
		 * stream buffer.
		 */
		uint32_t basepri = __get_BASEPRI();
		taskENTER_CRITICAL_FROM_ISR();

		xStreamBufferSendFromISR(
			write_stream_buffer, str, length, &resched1
		);

82
		taskEXIT_CRITICAL_FROM_ISR(basepri);
83
84
85
86
87
88
89
90
91
92
93
94

		if (serial_task_id != NULL) {
			xTaskNotifyFromISR(
				serial_task_id,
				SERIAL_WRITE_NOTIFY,
				eSetBits,
				&resched2
			);
		}

		/* Yield if this write woke up a higher priority task */
		portYIELD_FROM_ISR(resched1 || resched2);
95
	} else {
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
		size_t bytes_sent = 0;
		size_t index      = 0;
		do {
			taskENTER_CRITICAL();
			/*
			 * Wait time needs to be zero, because we are in a
			 * critical section.
			 */
			bytes_sent = xStreamBufferSend(
				write_stream_buffer,
				&str[index],
				length - index,
				0
			);
			index += bytes_sent;
			taskEXIT_CRITICAL();
			if (serial_task_id != NULL) {
				xTaskNotify(
					serial_task_id,
					SERIAL_WRITE_NOTIFY,
					eSetBits
				);
				portYIELD();
			}
		} while (index < length);
121
	}
Rahix's avatar
Rahix committed
122
123
}

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
static void serial_flush_from_isr(void)
{
	uint8_t rx_data[32];
	size_t received_bytes;

	BaseType_t resched = pdFALSE;
	BaseType_t woken   = pdFALSE;

	uint32_t basepri = __get_BASEPRI();
	taskENTER_CRITICAL_FROM_ISR();

	do {
		received_bytes = xStreamBufferReceiveFromISR(
			write_stream_buffer,
			(void *)rx_data,
			sizeof(rx_data),
			&woken
		);
		resched |= woken;

		if (received_bytes == 0) {
			break;
		}

		/*
		 * The SDK-driver for UART is not reentrant
		 * which means we need to perform UART writes
		 * in a critical section.
		 */

		UART_Write(ConsoleUart, (uint8_t *)&rx_data, received_bytes);
	} while (received_bytes > 0);

	taskEXIT_CRITICAL_FROM_ISR(basepri);

	portYIELD_FROM_ISR(&resched);
}

static void serial_flush_from_thread(void)
{
	uint8_t rx_data[32];
	size_t received_bytes;

	do {
		taskENTER_CRITICAL();
		received_bytes = xStreamBufferReceive(
			write_stream_buffer,
			(void *)rx_data,
			sizeof(rx_data),
			0
		);
		taskEXIT_CRITICAL();

		if (received_bytes == 0) {
			break;
		}

		/*
		 * The SDK-driver for UART is not reentrant
		 * which means we need to perform UART writes
		 * in a critical section.
		 */
		taskENTER_CRITICAL();
		UART_Write(ConsoleUart, (uint8_t *)&rx_data, received_bytes);
		taskEXIT_CRITICAL();

		cdcacm_write((uint8_t *)&rx_data, received_bytes);
		ble_uart_write((uint8_t *)&rx_data, received_bytes);
	} while (received_bytes > 0);
}

/*
 * Flush all characters which are currently waiting to be printed.
 *
 * If this function is called from an ISR, it will only flush to hardware UART
 * while a call from thread mode will flush to UART, CDC-ACM, and BLE Serial.
 */
void serial_flush(void)
{
	if (xPortIsInsideInterrupt()) {
		serial_flush_from_isr();
	} else {
		serial_flush_from_thread();
	}
}

Rahix's avatar
Rahix committed
210
/*
211
 * API-call to read a character from the queue.
Rahix's avatar
Rahix committed
212
 */
213
int epic_uart_read_char(void)
Rahix's avatar
Rahix committed
214
215
{
	char chr;
216
217
218
219
	if (xQueueReceive(read_queue, &chr, 0) == pdTRUE) {
		return (int)chr;
	}
	return (-1);
Rahix's avatar
Rahix committed
220
221
}

222
223
224
225
226
/*
 * API-call to read data from the queue.
 */
int epic_uart_read_str(char *buf, size_t cnt)
{
227
	size_t i = 0;
228
229
230
231
232
233
234
235
236
237

	for (i = 0; i < cnt; i++) {
		if (xQueueReceive(read_queue, &buf[i], 0) != pdTRUE) {
			break;
		}
	}

	return i;
}

238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
long _write_epicardium(int fd, const char *buf, size_t cnt)
{
	/*
	 * Only print one line at a time.  Insert `\r` between lines so they are
	 * properly displayed on the serial console.
	 */
	size_t i, last = 0;
	for (i = 0; i < cnt; i++) {
		if (buf[i] == '\n') {
			epic_uart_write_str(&buf[last], i - last);
			epic_uart_write_str("\r", 1);
			last = i;
		}
	}
	epic_uart_write_str(&buf[last], cnt - last);
	return cnt;
}

Rahix's avatar
Rahix committed
256
257
258
259
260
261
/* Interrupt handler needed for SDK UART implementation */
void UART0_IRQHandler(void)
{
	UART_Handler(ConsoleUart);
}

Rahix's avatar
Rahix committed
262
static void uart_callback(uart_req_t *req, int error)
Rahix's avatar
Rahix committed
263
264
{
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
265
266
267
268
269
270
	xTaskNotifyFromISR(
		serial_task_id,
		SERIAL_READ_NOTIFY,
		eSetBits,
		&xHigherPriorityTaskWoken
	);
Rahix's avatar
Rahix committed
271
272
273
	portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

274
void serial_enqueue_char(char chr)
Rahix's avatar
Rahix committed
275
276
277
{
	if (chr == 0x3) {
		/* Control-C */
278
		api_interrupt_trigger(EPIC_INT_CTRL_C);
279
280
	}

Rahix's avatar
Rahix committed
281
282
283
284
	if (xQueueSend(read_queue, &chr, 100) == errQUEUE_FULL) {
		/* Queue overran, wait a bit */
		vTaskDelay(portTICK_PERIOD_MS * 50);
	}
285
286

	api_interrupt_trigger(EPIC_INT_UART_RX);
Rahix's avatar
Rahix committed
287
288
}

Rahix's avatar
Rahix committed
289
void vSerialTask(void *pvParameters)
Rahix's avatar
Rahix committed
290
{
Rahix's avatar
Rahix committed
291
292
	serial_task_id = xTaskGetCurrentTaskHandle();

Rahix's avatar
Rahix committed
293
294
295
	/* Setup UART interrupt */
	NVIC_ClearPendingIRQ(UART0_IRQn);
	NVIC_DisableIRQ(UART0_IRQn);
Rahix's avatar
Rahix committed
296
	NVIC_SetPriority(UART0_IRQn, 6);
Rahix's avatar
Rahix committed
297
298
	NVIC_EnableIRQ(UART0_IRQn);

Rahix's avatar
Rahix committed
299
300
301
302
303
304
	unsigned char data;
	uart_req_t read_req = {
		.data     = &data,
		.len      = 1,
		.callback = uart_callback,
	};
Rahix's avatar
Rahix committed
305

Rahix's avatar
Rahix committed
306
307
308
	while (1) {
		int ret = UART_ReadAsync(ConsoleUart, &read_req);
		if (ret != E_NO_ERROR && ret != E_BUSY) {
309
			LOG_ERR("serial", "error reading uart: %d", ret);
Rahix's avatar
Rahix committed
310
			vTaskDelay(portMAX_DELAY);
Rahix's avatar
Rahix committed
311
312
		}

313
		ret = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
Rahix's avatar
Rahix committed
314

315
		if (ret & SERIAL_WRITE_NOTIFY) {
316
			serial_flush_from_thread();
Rahix's avatar
Rahix committed
317
		}
Rahix's avatar
Rahix committed
318

319
320
321
322
323
324
325
326
327
328
329
330
		if (ret & SERIAL_READ_NOTIFY) {
			if (read_req.num > 0) {
				serial_enqueue_char(*read_req.data);
			}

			while (UART_NumReadAvail(ConsoleUart) > 0) {
				serial_enqueue_char(UART_ReadByte(ConsoleUart));
			}

			while (cdcacm_num_read_avail() > 0) {
				serial_enqueue_char(cdcacm_read());
			}
Rahix's avatar
Rahix committed
331
		}
Rahix's avatar
Rahix committed
332
333
	}
}