Loading...
Searching...
No Matches
shell.c
1/***********************************************************************************
2 * @file shell.c *
3 * @author Matt Ricci *
4 * @addtogroup Shell *
5 * *
6 * @todo Add commands to buffer to allow managing shell history for frontend *
7 ***********************************************************************************/
8
9#include "shell.h"
10
11#include "string.h"
12
13#include "tasklist.h"
14#include "devicelist.h"
15#include "uart.h"
16
17extern uint32_t __shell_vector_start;
18extern uint32_t __shell_vector_end;
19
20static TaskHandle_t taskHandle; // Handle of currently active program in shell.
21static TaskHandle_t vShellProcessHandle; // Handle of shell processing task
22
23// TODO:
24// Add deviceReady flag to driver API to indicate
25// when a device struct is initialised and populated
26static UART_t *uart;
27
28char *prompt = "(CORE ACTIVE) Australis >: ";
29
30/* =============================================================================== */
41void vShellExec(void *argument) {
42
43 for (;;) {
44 Shell_run((char *)argument);
45 uart->print(uart, prompt);
46
47 // Remove handle from shell first to prevent
48 // nullptr dereference in Shell_clear
49 taskHandle = NULL;
50
51 // Delete task and end thread
52 vTaskDelete(NULL);
53 }
54}
55
56/* =============================================================================== */
64void vShellProcess(void *argument) {
65 CREATE_MESSAGE(rxMsg, UART_MSG_LENGTH);
66
67 rxMsg.length = 0; // Reset length first to indicate how much of the buffer is full
68 uint32_t rxData;
69
70 vShellProcessHandle = xTaskGetCurrentTaskHandle();
71
72 // Hang task until UART is initialised in device list
73 while (!(uart = DeviceList_getDeviceHandle(DEVICE_UART_USB).device));
74
75 char *display = "\0";
76 bool suspended = false;
77
78 for (;;) {
79 // Wait for new byte on UART
80 xTaskNotifyWait(0, 0, &rxData, portMAX_DELAY);
81
82 // Read in received byte to circular buffer
83 rxMsg.data[rxMsg.length++] = rxData;
84 rxMsg.length %= UART_MSG_LENGTH;
85
86 // Initialise display string with last received character
87 display = (char[]){rxData, '\0'};
88
89 // Clear terminal on <Ctrl-c>
90 if (rxData == SIGINT) {
92 rxMsg.length = 0; // Reset character index / string length
93 }
94
95 // Erase character and move cursor backwards on <BS>
96 if (rxData == BACKSPACE || rxData == DEL) {
97 // Put backspace on display
98 if (rxMsg.length > 1)
99 display = "\b \b";
100 rxMsg.length -= (rxMsg.length > 1) ? 2 : 1;
101 }
102
103 // Suspend scheduler on <Ctrl-z> outside of shell
104 if (rxData == SUBSTITUTE && !suspended) {
105 vTaskSuspend(TaskList_getTaskByName("HDataAcq"));
106 vTaskSuspend(TaskList_getTaskByName("LDataAcq"));
107 // TODO: Think of a good way of indicating this visually
108 // Maybe AustralisCore exposes core halt/run functions in
109 // private header?
110 // (CORE PAUSED)
111 uart->println(uart, "\r\nAustralis core paused.");
112 prompt = "(CORE PAUSED) Australis >: ";
113 if (taskHandle == NULL)
114 uart->print(uart, prompt);
115 rxMsg.length = 0;
116 suspended = true;
117 }
118 // Resume scheduler on <Ctrl-z> outside of shell
119 else if (rxData == SUBSTITUTE && suspended) {
120 vTaskResume(TaskList_getTaskByName("HDataAcq"));
121 vTaskResume(TaskList_getTaskByName("LDataAcq"));
122 // TODO: Think of a good way of indicating this visually
123 // Maybe AustralisCore exposes core halt/run functions in
124 // private header?
125 // (CORE ACTIVE)
126 uart->println(uart, "\r\nAustralis core resumed.");
127 prompt = "(CORE ACTIVE) Australis >: ";
128 if (taskHandle == NULL)
129 uart->print(uart, prompt);
130 rxMsg.length = 0;
131 suspended = false;
132 }
133 // Print display string otherwise
134 else {
135 uart->print(uart, display);
136 }
137
138 // Send command for execution and reset buffer on <Enter>
139 if (rxData == CARRIAGE_RETURN) {
140 char *termination = (char *)&rxMsg.data[rxMsg.length - 1];
141 *termination = '\0';
142 rxMsg.length = 0;
143 uart->print(uart, "\n");
144
145 // Pass command to shell and begin executing
146 xTaskCreate(
147 vShellExec, "ShellProgram", 512,
148 rxMsg.data, configMAX_PRIORITIES - 6,
149 &taskHandle
150 );
151 }
152 }
153}
154
155/* =============================================================================== */
167bool Shell_run(char *programName) {
168
169 // Early exit if uart is not initialised
170 if (uart == NULL)
171 return false;
172
173 char *token = strtok((char *)programName, " ");
174 char *flags = strchr(token, '\0') + 1;
175
176 // TODO:
177 // Make this a forEach function that operates on callback functions that are passed
178 // each handle. This should not be publically exposed and instead left private to
179 // the Australis core.
180 //
181 // Register programs in vector to shell
182 for (uint32_t *i = (uint32_t *)&__shell_vector_start; i < (uint32_t *)&__shell_vector_end; i++) {
183 // Dereference memory location and cast to program handle pointer
185 // Iterate shell vector and execute function from handle with matching name (if any)
186 if (!strcmp(handle->name, programName)) {
187 handle->exec(uart, flags);
188 return true; // Early exit if program is found
189 }
190 }
191
192 // Print help string if no matching command is found
193 uart->print(uart, (char *)programName);
194 uart->println(uart, ": command not recognized. Run `help` for a list of available commands");
195
196 return false;
197}
198
199/* =============================================================================== */
207
208 // Early exit if uart is not initialised
209 if (uart == NULL)
210 return false;
211
212 // Delete any running task
213 if (taskHandle != NULL) {
214 vTaskDelete(taskHandle);
215 taskHandle = NULL;
216 }
217
218 uart->print(uart, "\r\n");
219 uart->print(uart, prompt);
220
221 return true;
222}
223
224/* ============================================================================================== */
230void pubShellRxInterrupt() {
231 BaseType_t xHigherPriorityTaskWoken = pdFALSE;
232
233 // Exit if peripheral is not ready
234 if (uart == NULL)
235 goto UART_NOT_READY;
236
237 if (uart->interface->SR & USART_SR_RXNE) {
238 uint8_t data = uart->interface->DR;
239 xTaskNotifyFromISR(vShellProcessHandle, data, eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
240 portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
241 }
242
243UART_NOT_READY:
244 return;
245}
DeviceHandle_t DeviceList_getDeviceHandle(DeviceKey)
Retrieve device handle from list by key.
Definition devicelist.c:36
char name[SHELL_PROGRAM_NAME_LENGTH]
Program name as referenced by the shell.
Definition shell.h:61
void(* exec)(UART_t *, char *)
Program entry point function pointer.
Definition shell.h:62
void vShellProcess(void *argument)
Definition shell.c:64
bool Shell_run(char *programName)
Executes a shell program by name.
Definition shell.c:167
bool Shell_sigint()
Exit any running task and clear to new line.
Definition shell.c:206
Struct definition for shell program handle.
Definition shell.h:60
TaskHandle_t TaskList_getTaskByName(char *)
Retrieve task handle from list by name string.
Definition tasklist.c:24
Struct definition for UART interface.
Definition uart.h:132
#define CREATE_MESSAGE(name, length_)
Macro to define, on the stack, a named message struct.
Definition topic.h:59