2 * This file is part of INAV.
4 * INAV is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * INAV is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with INAV. If not, see <http://www.gnu.org/licenses/>.
23 #if defined(SEMIHOSTING) || defined(SITL_BUILD)
27 #include "build/version.h"
28 #include "build/debug.h"
30 #include "drivers/serial.h"
31 #include "drivers/time.h"
33 #include "common/log.h"
34 #include "common/printf.h"
35 #include "common/utils.h"
37 #include "config/feature.h"
38 #include "config/parameter_group_ids.h"
40 #include "io/serial.h"
42 #include "fc/config.h"
43 #include "fc/settings.h"
46 #include "msp/msp_serial.h"
47 #include "msp/msp_protocol.h"
51 #define LOG_PREFIX "[%6d.%03d] "
52 #define LOG_PREFIX_FORMATTED_SIZE 13
54 static serialPort_t
* logPort
= NULL
;
55 static mspPort_t
* mspLogPort
= NULL
;
57 PG_REGISTER(logConfig_t
, logConfig
, PG_LOG_CONFIG
, 0);
59 PG_RESET_TEMPLATE(logConfig_t
, logConfig
,
60 .level
= SETTING_LOG_LEVEL_DEFAULT
,
61 .topics
= SETTING_LOG_TOPICS_DEFAULT
66 const serialPortConfig_t
*portConfig
= findSerialPortConfig(FUNCTION_LOG
);
71 bool portIsSharedWithMSP
= false;
73 if (determinePortSharing(portConfig
, FUNCTION_LOG
) == PORTSHARING_SHARED
) {
74 // We support sharing a LOG port only with MSP
75 if (portConfig
->functionMask
!= (FUNCTION_LOG
| FUNCTION_MSP
)) {
78 portIsSharedWithMSP
= true;
81 // If the port is shared with MSP, reuse the port
82 if (portIsSharedWithMSP
) {
83 const serialPort_t
*logAndMspPort
= findSharedSerialPort(FUNCTION_LOG
, FUNCTION_MSP
);
88 mspLogPort
= mspSerialPortFind(logAndMspPort
);
94 logPort
= openSerialPort(portConfig
->identifier
, FUNCTION_LOG
, NULL
, NULL
, baudRates
[BAUD_921600
], MODE_TX
, SERIAL_NOT_INVERTED
);
99 // Initialization done
100 LOG_INFO(SYSTEM
, "%s/%s %s %s / %s (%s)",
110 static void logPutcp(void *p
, char ch
)
112 *(*((char **) p
))++ = ch
;
115 static void logPrint(const char *buf
, size_t size
)
117 #if defined(SEMIHOSTING)
118 static bool semihostingInitialized
= false;
119 extern void initialise_monitor_handles(void);
121 if (!semihostingInitialized
) {
122 initialise_monitor_handles();
123 semihostingInitialized
= true;
125 for (size_t ii
= 0; ii
< size
; ii
++) {
126 fputc(buf
[ii
], stdout
);
129 SD(printf("%s\n", buf
));
131 // Send data via UART (if configured & connected - a safeguard against zombie VCP)
132 if (serialIsConnected(logPort
)) {
133 serialPrint(logPort
, buf
);
135 } else if (mspLogPort
) {
136 mspSerialPushPort(MSP_DEBUGMSG
, (uint8_t*)buf
, size
, mspLogPort
, MSP_V2_NATIVE
);
140 static size_t logFormatPrefix(char *buf
, const timeMs_t timeMs
)
143 return tfp_sprintf(buf
, LOG_PREFIX
, (int)(timeMs
/ 1000), (int)(timeMs
% 1000));
146 static bool logHasOutput(void)
148 #if defined(SEMIHOSTING)
151 return logPort
|| mspLogPort
;
155 static bool logIsEnabled(logTopic_e topic
, unsigned level
)
157 return logHasOutput() && (level
<= logConfig()->level
|| (logConfig()->topics
& (1 << topic
)));
160 void _logf(logTopic_e topic
, unsigned level
, const char *fmt
, ...)
166 STATIC_ASSERT(MSP_PORT_OUTBUF_SIZE
>= sizeof(buf
), MSP_PORT_OUTBUF_SIZE_not_big_enough_for_log
);
168 if (!logIsEnabled(topic
, level
)) {
172 charCount
= logFormatPrefix(buf
, millis());
173 bufPtr
= &buf
[charCount
];
178 charCount
+= tfp_format(&bufPtr
, logPutcp
, fmt
, va
);
179 logPutcp(&bufPtr
, '\n');
180 logPutcp(&bufPtr
, 0);
184 logPrint(buf
, charCount
);
187 void _logBufferHex(logTopic_e topic
, unsigned level
, const void *buffer
, size_t size
)
189 // Print lines of up to maxBytes bytes. We need 5 characters per byte
191 const size_t charsPerByte
= 5;
192 const size_t maxBytes
= 8;
193 char buf
[LOG_PREFIX_FORMATTED_SIZE
+ charsPerByte
* maxBytes
+ 1]; // +1 for the null terminator
194 size_t bufPos
= LOG_PREFIX_FORMATTED_SIZE
;
195 const uint8_t *inputPtr
= buffer
;
197 if (!logIsEnabled(topic
, level
)) {
201 logFormatPrefix(buf
, millis());
203 for (size_t ii
= 0; ii
< size
; ii
++) {
204 tfp_sprintf(buf
+ bufPos
, "0x%02x ", inputPtr
[ii
]);
205 bufPos
+= charsPerByte
;
206 if (bufPos
== sizeof(buf
)-1) {
207 buf
[bufPos
-1] = '\n';
209 logPrint(buf
, bufPos
+ 1);
210 bufPos
= LOG_PREFIX_FORMATTED_SIZE
;
214 if (bufPos
> LOG_PREFIX_FORMATTED_SIZE
) {
215 buf
[bufPos
-1] = '\n';
217 logPrint(buf
, bufPos
+ 1);