2015-07-10 06:01:56 -07:00
|
|
|
/**
|
|
|
|
* @file console_io.cpp
|
|
|
|
*
|
|
|
|
* @date Dec 29, 2012
|
2020-01-13 18:57:43 -08:00
|
|
|
* @author Andrey Belomutskiy, (c) 2012-2020
|
2015-07-10 06:01:56 -07:00
|
|
|
*
|
|
|
|
* This file is part of rusEfi - see http://rusefi.com
|
|
|
|
*
|
2020-05-04 10:28:00 -07:00
|
|
|
* rusEFI can communicate with external universe via native USB or some sort of TTL mode
|
|
|
|
* We have an interesting situation with TTL communication channels, we have
|
|
|
|
* 1) SERIAL - this one was implemented first simply because the code was readily available (works on stm32)
|
|
|
|
* this one is most suitable for streaming HAL API
|
|
|
|
* this one is not great since each byte requires an IRQ and with enough IRQ delay we have a risk of data loss
|
|
|
|
* 2) UART DMA - the best one since FIFO buffer reduces data loss (works on stm32)
|
|
|
|
* We have two halves of DMA buffer - one is used for TTL while rusEFI prepares next batch of data in the other side.
|
|
|
|
* We need idle support in order to not wait for the complete buffer to get full in order to recieve a message.
|
|
|
|
* Back when we were implementing this STM32_DMA_CR_HTIE was not available in ChibiOS driver so we have added it.
|
|
|
|
* we have custom rusEFI changes to ChibiOS HAL driver v1
|
|
|
|
* F7 uses driver v2 which currently does not have rusEFI changes.
|
|
|
|
* open question if fresh ChibiOS is better in this regard.
|
|
|
|
* 3) UART this one is useful on platforms with hardware FIFO buffer like Kinetis.
|
|
|
|
* stm32 does not have such buffer so for stm32 UART without DMA has no advantages
|
|
|
|
*
|
|
|
|
*
|
2015-07-10 06:01:56 -07:00
|
|
|
* rusEfi is free software; you can redistribute it and/or modify it under the terms of
|
|
|
|
* the GNU General Public License as published by the Free Software Foundation; either
|
|
|
|
* version 3 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
|
|
|
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with this program.
|
|
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2019-03-29 07:29:01 -07:00
|
|
|
#include "engine.h"
|
2015-07-10 06:01:56 -07:00
|
|
|
#include "console_io.h"
|
2019-07-06 17:15:49 -07:00
|
|
|
#include "os_util.h"
|
2015-07-10 06:01:56 -07:00
|
|
|
#include "tunerstudio.h"
|
|
|
|
|
2019-04-12 19:10:57 -07:00
|
|
|
#if EFI_SIMULATOR
|
2015-07-10 06:01:56 -07:00
|
|
|
#include "rusEfiFunctionalTest.h"
|
2017-01-05 01:03:02 -08:00
|
|
|
#endif /*EFI_SIMULATOR */
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2016-02-14 09:03:48 -08:00
|
|
|
EXTERN_ENGINE;
|
|
|
|
|
2019-04-12 19:10:57 -07:00
|
|
|
#if HAL_USE_SERIAL_USB
|
2015-07-10 06:01:56 -07:00
|
|
|
#include "usbcfg.h"
|
|
|
|
#include "usbconsole.h"
|
|
|
|
extern SerialUSBDriver SDU1;
|
2017-01-05 01:03:02 -08:00
|
|
|
#endif /* HAL_USE_SERIAL_USB */
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2019-05-05 08:06:27 -07:00
|
|
|
// 10 seconds
|
|
|
|
#define CONSOLE_WRITE_TIMEOUT 10000
|
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
static bool isSerialConsoleStarted = false;
|
|
|
|
|
2016-02-04 12:01:45 -08:00
|
|
|
static event_listener_t consoleEventListener;
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2016-02-13 18:02:14 -08:00
|
|
|
bool consoleByteArrived = false;
|
|
|
|
|
|
|
|
void onDataArrived(void) {
|
|
|
|
consoleByteArrived = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
/**
|
|
|
|
* @brief Reads a whole line from the input channel.
|
|
|
|
*
|
|
|
|
* @param[in] chp pointer to a @p BaseChannel object
|
|
|
|
* @param[in] line pointer to the line buffer
|
|
|
|
* @param[in] size buffer maximum length
|
|
|
|
* @return The operation status.
|
|
|
|
* @retval TRUE the channel was reset or CTRL-D pressed.
|
|
|
|
* @retval FALSE operation successful.
|
|
|
|
*/
|
2019-05-04 07:31:04 -07:00
|
|
|
/* let's keep this dead code for a bit
|
2015-07-10 06:01:56 -07:00
|
|
|
static bool getConsoleLine(BaseSequentialStream *chp, char *line, unsigned size) {
|
|
|
|
char *p = line;
|
|
|
|
|
|
|
|
while (true) {
|
2017-01-05 01:03:02 -08:00
|
|
|
if (!isCommandLineConsoleReady()) {
|
2019-04-01 14:04:49 -07:00
|
|
|
// we better do not read from serial before it is ready
|
2015-07-10 06:01:56 -07:00
|
|
|
chThdSleepMilliseconds(10);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-01-23 18:17:30 -08:00
|
|
|
short c = (short) streamGet(chp);
|
2016-02-13 18:02:14 -08:00
|
|
|
onDataArrived();
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2019-05-05 08:06:27 -07:00
|
|
|
#if defined(EFI_CONSOLE_SERIAL_DEVICE)
|
2019-04-01 08:39:25 -07:00
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
uint32_t flags;
|
|
|
|
chSysLock()
|
|
|
|
;
|
|
|
|
|
|
|
|
flags = chEvtGetAndClearFlagsI(&consoleEventListener);
|
|
|
|
chSysUnlock()
|
|
|
|
;
|
|
|
|
if (flags & SD_OVERRUN_ERROR) {
|
2016-10-10 11:02:17 -07:00
|
|
|
// firmwareError(OBD_PCM_Processor_Fault, "serial overrun");
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
2019-04-01 08:39:25 -07:00
|
|
|
|
2017-05-23 10:10:43 -07:00
|
|
|
#endif
|
2015-07-10 06:01:56 -07:00
|
|
|
|
|
|
|
#if EFI_UART_ECHO_TEST_MODE
|
2019-05-04 07:31:04 -07:00
|
|
|
// That's test code - let's test connectivity
|
2015-07-10 06:01:56 -07:00
|
|
|
consolePutChar((uint8_t) c);
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (c < 0 || c == 4) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (c == 8) {
|
|
|
|
if (p != line) {
|
|
|
|
// backspace
|
|
|
|
consolePutChar((uint8_t) c);
|
|
|
|
consolePutChar(0x20);
|
|
|
|
consolePutChar((uint8_t) c);
|
|
|
|
p--;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (c == '\r') {
|
|
|
|
consolePutChar('\r');
|
|
|
|
consolePutChar('\n');
|
|
|
|
*p = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (c == '\n') {
|
|
|
|
consolePutChar('\n');
|
|
|
|
*p = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (c < 0x20) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (p < line + size - 1) {
|
|
|
|
consolePutChar((uint8_t) c);
|
|
|
|
*p++ = (char) c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-05-04 07:31:04 -07:00
|
|
|
*/
|
2015-07-10 06:01:56 -07:00
|
|
|
CommandHandler console_line_callback;
|
|
|
|
|
2019-05-05 08:06:27 -07:00
|
|
|
#if (defined(EFI_CONSOLE_SERIAL_DEVICE) && ! EFI_SIMULATOR )
|
2020-05-02 22:43:39 -07:00
|
|
|
SerialConfig serialConfig = { 0, 0, USART_CR2_STOP1_BITS | USART_CR2_LINEN, 0 };
|
2015-07-10 06:01:56 -07:00
|
|
|
#endif
|
|
|
|
|
2019-05-05 08:06:27 -07:00
|
|
|
#if (defined(EFI_CONSOLE_UART_DEVICE) && ! EFI_SIMULATOR )
|
|
|
|
/* Note: This structure is modified from the default ChibiOS layout! */
|
2020-05-03 21:10:20 -07:00
|
|
|
UARTConfig uartConfig = {
|
2019-05-05 08:06:27 -07:00
|
|
|
.txend1_cb = NULL, .txend2_cb = NULL, .rxend_cb = NULL, .rxchar_cb = NULL, .rxerr_cb = NULL,
|
|
|
|
.speed = 0, .cr1 = 0, .cr2 = 0/*USART_CR2_STOP1_BITS*/ | USART_CR2_LINEN, .cr3 = 0,
|
|
|
|
.timeout_cb = NULL, .rxhalf_cb = NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
// To use UART driver instead of Serial, we need to imitate "BaseChannel" streaming functionality
|
|
|
|
static msg_t _putt(void *, uint8_t b, sysinterval_t timeout) {
|
|
|
|
int n = 1;
|
|
|
|
uartSendTimeout(EFI_CONSOLE_UART_DEVICE, (size_t *)&n, &b, timeout);
|
|
|
|
return MSG_OK;
|
|
|
|
}
|
|
|
|
static size_t _writet(void *, const uint8_t *bp, size_t n, sysinterval_t timeout) {
|
|
|
|
uartSendTimeout(EFI_CONSOLE_UART_DEVICE, (size_t *)&n, bp, timeout);
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
static msg_t _put(void *ip, uint8_t b) {
|
|
|
|
#ifdef UART_USE_BLOCKING_SEND
|
|
|
|
// this version can be called from the locked state (no interrupts)
|
|
|
|
uart_lld_blocking_send(EFI_CONSOLE_UART_DEVICE, 1, (void *)&b);
|
|
|
|
#else
|
|
|
|
// uartSendTimeout() needs interrupts to wait for the end of transfer, so we have to unlock them temporary
|
|
|
|
bool wasLocked = isLocked();
|
|
|
|
if (wasLocked)
|
|
|
|
unlockAnyContext();
|
|
|
|
_putt(ip, b, CONSOLE_WRITE_TIMEOUT);
|
|
|
|
if (wasLocked)
|
|
|
|
lockAnyContext();
|
|
|
|
#endif /* UART_USE_BLOCKING_WRITE */
|
|
|
|
return MSG_OK;
|
|
|
|
}
|
|
|
|
static size_t _write(void *ip, const uint8_t *bp, size_t n) {
|
|
|
|
return _writet(ip, bp, n, CONSOLE_WRITE_TIMEOUT);
|
|
|
|
}
|
|
|
|
static msg_t _gett(void *, sysinterval_t /*timeout*/) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static size_t _readt(void *, uint8_t */*bp*/, size_t /*n*/, sysinterval_t /*timeout*/) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static msg_t _get(void *) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static size_t _read(void *, uint8_t */*bp*/, size_t /*n*/) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static msg_t _ctl(void *, unsigned int /*operation*/, void */*arg*/) {
|
|
|
|
return MSG_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is a "fake" channel for getConsoleChannel() filled with our handlers
|
|
|
|
static const struct BaseChannelVMT uartChannelVmt = {
|
|
|
|
.instance_offset = (size_t)0, .write = _write, .read = _read, .put = _put, .get = _get,
|
|
|
|
.putt = _putt, .gett = _gett, .writet = _writet, .readt = _readt, .ctl = _ctl
|
|
|
|
};
|
|
|
|
static const BaseChannel uartChannel = { .vmt = &uartChannelVmt };
|
|
|
|
#endif /* EFI_CONSOLE_UART_DEVICE */
|
|
|
|
|
2019-04-12 19:10:57 -07:00
|
|
|
#if EFI_PROD_CODE || EFI_EGT
|
2017-01-05 01:03:02 -08:00
|
|
|
|
2019-04-01 11:18:21 -07:00
|
|
|
bool isUsbSerial(BaseChannel * channel) {
|
2019-04-12 19:10:57 -07:00
|
|
|
#if HAL_USE_SERIAL_USB
|
2019-04-01 11:18:21 -07:00
|
|
|
return channel == (BaseChannel *) &CONSOLE_USB_DEVICE;
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2017-05-23 10:10:43 -07:00
|
|
|
BaseChannel * getConsoleChannel(void) {
|
2019-05-05 08:06:27 -07:00
|
|
|
#if defined(EFI_CONSOLE_SERIAL_DEVICE)
|
|
|
|
return (BaseChannel *) EFI_CONSOLE_SERIAL_DEVICE;
|
|
|
|
#endif /* EFI_CONSOLE_SERIAL_DEVICE */
|
|
|
|
|
2019-04-12 19:10:57 -07:00
|
|
|
#if defined(EFI_CONSOLE_UART_DEVICE)
|
2019-05-05 08:06:27 -07:00
|
|
|
return (BaseChannel *) &uartChannel;
|
2015-07-10 06:01:56 -07:00
|
|
|
#endif /* EFI_CONSOLE_UART_DEVICE */
|
|
|
|
|
2019-04-12 19:10:57 -07:00
|
|
|
#if HAL_USE_SERIAL_USB
|
2019-04-01 11:18:21 -07:00
|
|
|
return (BaseChannel *) &CONSOLE_USB_DEVICE;
|
2015-07-10 06:01:56 -07:00
|
|
|
#else
|
|
|
|
return NULL;
|
2017-01-05 01:03:02 -08:00
|
|
|
#endif /* HAL_USE_SERIAL_USB */
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2017-01-05 01:03:02 -08:00
|
|
|
bool isCommandLineConsoleReady(void) {
|
2019-04-01 08:39:25 -07:00
|
|
|
return isSerialConsoleStarted;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
#endif /* EFI_PROD_CODE || EFI_EGT */
|
|
|
|
|
2019-04-12 19:10:57 -07:00
|
|
|
#if !defined(EFI_CONSOLE_NO_THREAD)
|
2019-03-30 14:41:46 -07:00
|
|
|
|
2020-05-02 22:43:39 -07:00
|
|
|
ts_channel_s primaryChannel;
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2015-11-24 20:03:17 -08:00
|
|
|
static THD_WORKING_AREA(consoleThreadStack, 3 * UTILITY_THREAD_STACK_SIZE);
|
2019-04-01 14:04:49 -07:00
|
|
|
static THD_FUNCTION(consoleThreadEntryPoint, arg) {
|
2015-07-10 06:01:56 -07:00
|
|
|
(void) arg;
|
|
|
|
chRegSetThreadName("console thread");
|
|
|
|
|
2020-05-02 21:20:54 -07:00
|
|
|
primaryChannel.channel = (BaseChannel *) getConsoleChannel();
|
|
|
|
if (primaryChannel.channel != NULL) {
|
2019-04-12 19:10:57 -07:00
|
|
|
#if EFI_TUNER_STUDIO
|
2020-05-02 21:20:54 -07:00
|
|
|
runBinaryProtocolLoop(&primaryChannel);
|
2019-04-01 09:11:57 -07:00
|
|
|
#endif /* EFI_TUNER_STUDIO */
|
|
|
|
}
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2019-03-30 14:41:46 -07:00
|
|
|
#endif /* EFI_CONSOLE_NO_THREAD */
|
|
|
|
|
2016-02-13 14:02:32 -08:00
|
|
|
void consolePutChar(int x) {
|
|
|
|
chnWriteTimeout(getConsoleChannel(), (const uint8_t *)&x, 1, CONSOLE_WRITE_TIMEOUT);
|
|
|
|
}
|
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
void consoleOutputBuffer(const uint8_t *buf, int size) {
|
|
|
|
#if !EFI_UART_ECHO_TEST_MODE
|
2020-03-22 16:06:29 -07:00
|
|
|
chnWriteTimeout(getConsoleChannel(), buf, size, CONSOLE_WRITE_TIMEOUT);
|
2015-07-10 06:01:56 -07:00
|
|
|
#endif /* EFI_UART_ECHO_TEST_MODE */
|
|
|
|
}
|
|
|
|
|
|
|
|
static Logging *logger;
|
|
|
|
|
|
|
|
void startConsole(Logging *sharedLogger, CommandHandler console_line_callback_p) {
|
|
|
|
logger = sharedLogger;
|
|
|
|
console_line_callback = console_line_callback_p;
|
|
|
|
|
2020-06-21 13:37:33 -07:00
|
|
|
#if (defined(EFI_CONSOLE_SERIAL_DEVICE) || defined(EFI_CONSOLE_UART_DEVICE)) && ! EFI_SIMULATOR
|
|
|
|
efiSetPadMode("console RX", EFI_CONSOLE_RX_BRAIN_PIN, PAL_MODE_ALTERNATE(EFI_CONSOLE_AF));
|
|
|
|
efiSetPadMode("console TX", EFI_CONSOLE_TX_BRAIN_PIN, PAL_MODE_ALTERNATE(EFI_CONSOLE_AF));
|
|
|
|
isSerialConsoleStarted = true;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2019-05-05 08:06:27 -07:00
|
|
|
#if (defined(EFI_CONSOLE_SERIAL_DEVICE) && ! EFI_SIMULATOR)
|
2015-07-10 06:01:56 -07:00
|
|
|
/*
|
2017-06-04 08:31:20 -07:00
|
|
|
* Activates the serial
|
2015-07-10 06:01:56 -07:00
|
|
|
* it is important to set 'NONE' as flow control! in terminal application on the PC
|
|
|
|
*/
|
2017-06-04 08:31:20 -07:00
|
|
|
serialConfig.speed = engineConfiguration->uartConsoleSerialSpeed;
|
2019-05-05 08:06:27 -07:00
|
|
|
sdStart(EFI_CONSOLE_SERIAL_DEVICE, &serialConfig);
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2019-05-05 08:06:27 -07:00
|
|
|
chEvtRegisterMask((event_source_t *) chnGetEventSource(EFI_CONSOLE_SERIAL_DEVICE), &consoleEventListener, 1);
|
|
|
|
#elif (defined(EFI_CONSOLE_UART_DEVICE) && ! EFI_SIMULATOR)
|
|
|
|
uartConfig.speed = engineConfiguration->uartConsoleSerialSpeed;
|
|
|
|
uartStart(EFI_CONSOLE_UART_DEVICE, &uartConfig);
|
|
|
|
#endif /* EFI_CONSOLE_SERIAL_DEVICE || EFI_CONSOLE_UART_DEVICE */
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2019-04-12 19:10:57 -07:00
|
|
|
#if !defined(EFI_CONSOLE_NO_THREAD)
|
2019-04-01 14:04:49 -07:00
|
|
|
chThdCreateStatic(consoleThreadStack, sizeof(consoleThreadStack), NORMALPRIO, (tfunc_t)consoleThreadEntryPoint, NULL);
|
2019-03-30 14:41:46 -07:00
|
|
|
#endif /* EFI_CONSOLE_NO_THREAD */
|
2019-04-01 08:57:30 -07:00
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|