serial.c 5.74 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
}

Rahix's avatar
Rahix committed
124
/*
125
 * API-call to read a character from the queue.
Rahix's avatar
Rahix committed
126
 */
127
int epic_uart_read_char(void)
Rahix's avatar
Rahix committed
128
129
{
	char chr;
130
131
132
133
	if (xQueueReceive(read_queue, &chr, 0) == pdTRUE) {
		return (int)chr;
	}
	return (-1);
Rahix's avatar
Rahix committed
134
135
}

136
137
138
139
140
/*
 * API-call to read data from the queue.
 */
int epic_uart_read_str(char *buf, size_t cnt)
{
141
	size_t i = 0;
142
143
144
145
146
147
148
149
150
151

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

	return i;
}

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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
170
171
172
173
174
175
/* Interrupt handler needed for SDK UART implementation */
void UART0_IRQHandler(void)
{
	UART_Handler(ConsoleUart);
}

Rahix's avatar
Rahix committed
176
static void uart_callback(uart_req_t *req, int error)
Rahix's avatar
Rahix committed
177
178
{
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
179
180
181
182
183
184
	xTaskNotifyFromISR(
		serial_task_id,
		SERIAL_READ_NOTIFY,
		eSetBits,
		&xHigherPriorityTaskWoken
	);
Rahix's avatar
Rahix committed
185
186
187
	portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

188
void serial_enqueue_char(char chr)
Rahix's avatar
Rahix committed
189
190
191
{
	if (chr == 0x3) {
		/* Control-C */
192
		api_interrupt_trigger(EPIC_INT_CTRL_C);
193
194
	}

Rahix's avatar
Rahix committed
195
196
197
198
	if (xQueueSend(read_queue, &chr, 100) == errQUEUE_FULL) {
		/* Queue overran, wait a bit */
		vTaskDelay(portTICK_PERIOD_MS * 50);
	}
199
200

	api_interrupt_trigger(EPIC_INT_UART_RX);
Rahix's avatar
Rahix committed
201
202
}

Rahix's avatar
Rahix committed
203
void vSerialTask(void *pvParameters)
Rahix's avatar
Rahix committed
204
{
Rahix's avatar
Rahix committed
205
206
	serial_task_id = xTaskGetCurrentTaskHandle();

Rahix's avatar
Rahix committed
207
208
209
	/* Setup UART interrupt */
	NVIC_ClearPendingIRQ(UART0_IRQn);
	NVIC_DisableIRQ(UART0_IRQn);
Rahix's avatar
Rahix committed
210
	NVIC_SetPriority(UART0_IRQn, 6);
Rahix's avatar
Rahix committed
211
212
	NVIC_EnableIRQ(UART0_IRQn);

Rahix's avatar
Rahix committed
213
214
215
216
217
218
	unsigned char data;
	uart_req_t read_req = {
		.data     = &data,
		.len      = 1,
		.callback = uart_callback,
	};
Rahix's avatar
Rahix committed
219

220
221
222
	uint8_t rx_data[20];
	size_t received_bytes;

Rahix's avatar
Rahix committed
223
224
225
	while (1) {
		int ret = UART_ReadAsync(ConsoleUart, &read_req);
		if (ret != E_NO_ERROR && ret != E_BUSY) {
226
			LOG_ERR("serial", "error reading uart: %d", ret);
Rahix's avatar
Rahix committed
227
			vTaskDelay(portMAX_DELAY);
Rahix's avatar
Rahix committed
228
229
		}

230
		ret = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
Rahix's avatar
Rahix committed
231

232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
		if (ret & SERIAL_WRITE_NOTIFY) {
			do {
				received_bytes = xStreamBufferReceive(
					write_stream_buffer,
					(void *)rx_data,
					sizeof(rx_data),
					0
				);

				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();
257

258
259
260
261
262
263
264
				cdcacm_write(
					(uint8_t *)&rx_data, received_bytes
				);
				ble_uart_write(
					(uint8_t *)&rx_data, received_bytes
				);
			} while (received_bytes > 0);
Rahix's avatar
Rahix committed
265
		}
Rahix's avatar
Rahix committed
266

267
268
269
270
271
272
273
274
275
276
277
278
		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
279
		}
Rahix's avatar
Rahix committed
280
281
	}
}