CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
cdc_log.cpp
Go to the documentation of this file.
1
5#include "cdc_log.h"
6#include <stdlib.h>
7#include <string.h>
8#include <fcntl.h>
9#include <unistd.h>
10#include "sdkconfig.h"
11#include "esp_timer.h"
12#include "esp_attr.h"
13#include "freertos/FreeRTOS.h"
14#include "freertos/task.h"
15
16#if CONFIG_TINYUSB_CDC_ENABLED
17#include "tusb.h"
18#endif
19
20#ifndef DEBUG_MODE
21#define DEBUG_MODE 1
22#endif
23
24#if DEBUG_MODE
25static log_level_t s_log_level = CDC_LOG_LEVEL_DEBUG;
26#else
27static log_level_t s_log_level = CDC_LOG_LEVEL_WARN;
28#endif
29static bool s_initialized = false;
30
36
37static const char* level_str[] = {
38 "", // NONE
39 "E", // ERROR
40 "W", // WARN
41 "I", // INFO
42 "D", // DEBUG
43 "V" // VERBOSE
44};
45
47EXT_RAM_BSS_ATTR static error_log_entry_t s_error_log[ERROR_LOG_MAX_ENTRIES];
48static size_t s_error_log_head = 0; // Next write position
49static size_t s_error_log_count = 0; // Number of entries
50
56static void error_log_add(log_level_t level, const char* message) {
57 if (level != CDC_LOG_LEVEL_ERROR && level != CDC_LOG_LEVEL_WARN) return;
58 if (!message) return;
59
60 // Write to current position (overwrites oldest if full)
61 error_log_entry_t* entry = &s_error_log[s_error_log_head];
62 entry->timestamp_ms = (uint32_t)(esp_timer_get_time() / 1000);
63 entry->level = level;
64 strncpy(entry->message, message, ERROR_LOG_LINE_LEN - 1);
65 entry->message[ERROR_LOG_LINE_LEN - 1] = '\0';
66
67 // Advance head (ring buffer)
71 }
72}
73
80size_t error_log_get_entries(error_log_entry_t* entries, size_t max_entries) {
81 if (!entries || max_entries == 0) return 0;
82
83 size_t count = 0;
84 // Calculate start position (oldest entry)
86
87 for (size_t i = 0; i < s_error_log_count && count < max_entries; i++) {
88 size_t idx = (start + i) % ERROR_LOG_MAX_ENTRIES;
89 entries[count++] = s_error_log[idx];
90 }
91 return count;
92}
93
98size_t error_log_get_count(void) {
99 return s_error_log_count;
100}
101
105void error_log_clear(void) {
108}
109
113void error_log_dump(void) {
114 if (s_error_log_count == 0) {
115 console_printf("Error log: (empty)\r\n");
116 return;
117 }
118
119 console_printf("Error log (%zu entries):\r\n", s_error_log_count);
120
122 for (size_t i = 0; i < s_error_log_count; i++) {
123 size_t idx = (start + i) % ERROR_LOG_MAX_ENTRIES;
124 error_log_entry_t* e = &s_error_log[idx];
125
126 uint32_t secs = e->timestamp_ms / 1000;
127 uint32_t mins = secs / 60;
128 uint32_t hours = mins / 60;
129 console_printf("[%02lu:%02lu:%02lu][%s] %s\r\n",
130 hours % 24, mins % 60, secs % 60,
131 e->level == CDC_LOG_LEVEL_ERROR ? "E" : "W",
132 e->message);
133 }
134}
135
137
141void log_init(void) {
142#if DEBUG_MODE
143 s_log_level = CDC_LOG_LEVEL_DEBUG;
144#else
145 s_log_level = CDC_LOG_LEVEL_WARN;
146#endif
147 console_init();
148}
149
154void log_set_level(log_level_t level) {
155 s_log_level = level;
156}
157
162log_level_t log_get_level(void) {
163 return s_log_level;
164}
165
172void log_write(log_level_t level, const char* tag, const char* fmt, ...) {
173 // Always capture ERROR/WARN to error log
174 bool capture = (level == CDC_LOG_LEVEL_ERROR || level == CDC_LOG_LEVEL_WARN);
175
176 // Format message
177 char buf[256];
178 va_list args;
179 va_start(args, fmt);
180 vsnprintf(buf, sizeof(buf), fmt, args);
181 va_end(args);
182
183 // Format with prefix
184 char line[300];
185 snprintf(line, sizeof(line), "[%s][%s] %s",
186 level_str[level], tag ? tag : "???", buf);
187
188 // Capture to error log (before suppression check)
189 if (capture) {
190 error_log_add(level, line);
191 }
192
193 // Suppress INFO/DEBUG/VERBOSE while the auth-gate hook (if installed)
194 // reports an unauthenticated state. ERROR/WARN always emit.
195 if (level > CDC_LOG_LEVEL_WARN && s_authgate_hook && !s_authgate_hook()) {
196 return;
197 }
198
199 // Output if not suppressed
200 if (level <= s_log_level) {
201 console_printf("%s\n", line);
202 }
203}
204
209void log_raw(const char* fmt, ...) {
210 char buf[256];
211 va_list args;
212 va_start(args, fmt);
213 vsnprintf(buf, sizeof(buf), fmt, args);
214 va_end(args);
215 console_print(buf);
216}
217
225void log_hex(const char* tag, const char* label, const uint8_t* data, size_t len) {
226#if !DEBUG_MODE
227 // Release build: hex dumps can leak key material, swallow silently.
228 (void)tag; (void)label; (void)data; (void)len;
229#else
230 console_printf("[D][%s] %s (%zu bytes): ", tag ? tag : "HEX", label ? label : "data", len);
231 for (size_t i = 0; i < len; i++) {
232 console_printf("%02X", data[i]);
233 if ((i + 1) % 32 == 0 && (i + 1) < len) {
234 console_print("\n ");
235 } else if ((i + 1) % 4 == 0 && (i + 1) < len) {
236 console_putchar(' ');
237 }
238 }
239 console_print("\n");
240#endif
241}
242
246void console_init(void) {
247 if (s_initialized) return;
248
249 // Set stdin to non-blocking for UART fallback
250 int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
251 if (flags >= 0) {
252 fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
253 }
254
255 s_initialized = true;
256}
257
263 if (!s_initialized) return false;
264
265#if CONFIG_TINYUSB_CDC_ENABLED
266 if (tud_cdc_connected() && tud_cdc_available() > 0) {
267 return true;
268 }
269#endif
270
271 // Check input hook (e.g., BLE)
273 return true;
274 }
275
276 return false;
277}
278
284 if (!s_initialized) return -1;
285
286#if CONFIG_TINYUSB_CDC_ENABLED
287 // Priority 1: USB CDC
288 if (tud_cdc_connected() && tud_cdc_available()) {
289 return tud_cdc_read_char();
290 }
291#endif
292
293 // Priority 2: Input hook (e.g., BLE)
295 int c = s_input_getchar_hook();
296 if (c >= 0) {
297 return c;
298 }
299 }
300
301 // Fallback: UART via stdin
302 int c = getchar();
303 if (c != EOF) {
304 return c;
305 }
306 return -1;
307}
308
313void console_print(const char* str) {
314 if (!str) return;
315 size_t len = strlen(str);
316
317 // Always output to UART (visible via USB-Serial-JTAG or external adapter)
318 printf("%s", str);
319 fflush(stdout);
320
321#if CONFIG_TINYUSB_CDC_ENABLED
322 // Also send to USB CDC if connected. If the host stops reading the TX
323 // FIFO fills up; without a bound the original `while (avail == 0)
324 // continue;` loop deadlocks the caller — and because logging happens on
325 // every task this drags the whole UART driver lock along, freezing the
326 // device end-to-end. We give the FIFO a short retry window and then drop
327 // the rest of this log line: dropping a log entry is non-fatal, but
328 // hanging on it is.
329 if (s_initialized && tud_cdc_connected()) {
330 size_t written = 0;
331 const TickType_t deadline =
332 xTaskGetTickCount() + pdMS_TO_TICKS(20);
333 while (written < len) {
334 size_t avail = tud_cdc_write_available();
335 if (avail == 0) {
336 tud_cdc_write_flush();
337 if (xTaskGetTickCount() >= deadline) break;
338 vTaskDelay(1);
339 continue;
340 }
341 size_t to_write = len - written;
342 if (to_write > avail) to_write = avail;
343 written += tud_cdc_write(str + written, to_write);
344 }
345 tud_cdc_write_flush();
346 }
347#endif
348
349 // Also send to output hook (e.g., BLE)
350 if (s_output_hook) {
351 s_output_hook(str, len);
352 }
353}
354
359void console_printf(const char* fmt, ...) {
360 if (!fmt) return;
361
362 char buf[256];
363 va_list args;
364 va_start(args, fmt);
365 va_list args_copy;
366 va_copy(args_copy, args);
367 int len = vsnprintf(buf, sizeof(buf), fmt, args);
368 va_end(args);
369 if (len > 0 && static_cast<size_t>(len) >= sizeof(buf)) {
370 char* heap = static_cast<char*>(malloc(static_cast<size_t>(len) + 1));
371 if (heap) {
372 vsnprintf(heap, static_cast<size_t>(len) + 1, fmt, args_copy);
373 console_print(heap);
374 free(heap);
375 va_end(args_copy);
376 return;
377 }
378 }
379 va_end(args_copy);
380 console_print(buf);
381}
382
387void console_putchar(char c) {
388 putchar(c);
389 fflush(stdout); // Immediate echo for serial terminal
390
391#if CONFIG_TINYUSB_CDC_ENABLED
392 if (s_initialized && tud_cdc_connected()) {
393 tud_cdc_write_char(c);
394 tud_cdc_write_flush(); // Immediate echo for USB CDC
395 }
396#endif
397
398 // Also send to output hook (e.g., BLE)
399 if (s_output_hook) {
400 s_output_hook(&c, 1);
401 }
402}
403
407void console_flush(void) {
408#if CONFIG_TINYUSB_CDC_ENABLED
409 if (s_initialized && tud_cdc_connected()) {
410 tud_cdc_write_flush();
411 }
412#endif
413 fflush(stdout);
414}
415
424
431 console_input_getchar_hook_t getchar_hook) {
432 s_input_avail_hook = avail_hook;
433 s_input_getchar_hook = getchar_hook;
434}
435
uint8_t flags
static void error_log_add(log_level_t level, const char *message)
Adds warning/error entry to PSRAM-backed ring log.
Definition cdc_log.cpp:56
size_t error_log_get_entries(error_log_entry_t *entries, size_t max_entries)
Copies stored error-log entries in chronological order.
Definition cdc_log.cpp:80
void log_init(void)
Logging API implementation.
Definition cdc_log.cpp:141
size_t error_log_get_count(void)
Returns number of buffered error-log entries.
Definition cdc_log.cpp:98
int console_getchar(void)
Reads one character from available console input source.
Definition cdc_log.cpp:283
void console_register_input_hook(console_input_available_hook_t avail_hook, console_input_getchar_hook_t getchar_hook)
Registers optional additional input transport hooks.
Definition cdc_log.cpp:430
log_level_t log_get_level(void)
Returns current log verbosity threshold.
Definition cdc_log.cpp:162
bool console_available(void)
Returns whether any console input source has pending data.
Definition cdc_log.cpp:262
void console_printf(const char *fmt,...)
Formatted write helper for console output.
Definition cdc_log.cpp:359
static log_level_t s_log_level
Definition cdc_log.cpp:25
static const char * level_str[]
Definition cdc_log.cpp:37
void error_log_dump(void)
Dumps buffered error-log entries to console.
Definition cdc_log.cpp:113
static console_output_hook_t s_output_hook
Optional console hooks for additional I/O transports (for example BLE).
Definition cdc_log.cpp:32
static error_log_entry_t s_error_log[ERROR_LOG_MAX_ENTRIES]
PSRAM-backed error/warn ring log storage (no heap allocation).
Definition cdc_log.cpp:47
void log_set_level(log_level_t level)
Sets runtime log verbosity threshold.
Definition cdc_log.cpp:154
void log_write(log_level_t level, const char *tag, const char *fmt,...)
Writes formatted tagged log line with optional suppression.
Definition cdc_log.cpp:172
static log_authgate_hook_t s_authgate_hook
Definition cdc_log.cpp:34
static bool s_initialized
Definition cdc_log.cpp:29
static size_t s_error_log_head
Definition cdc_log.cpp:48
void console_print(const char *str)
Writes string to active console outputs.
Definition cdc_log.cpp:313
void console_register_output_hook(console_output_hook_t hook)
Console hook registration API.
Definition cdc_log.cpp:421
void console_flush(void)
Flushes buffered console output transports.
Definition cdc_log.cpp:407
void log_register_authgate_hook(log_authgate_hook_t hook)
Installs the auth-gate hook used to suppress INFO/DEBUG/VERBOSE output while a session is unauthentic...
Definition cdc_log.cpp:442
void console_putchar(char c)
Writes single character to active console outputs.
Definition cdc_log.cpp:387
void console_init(void)
Initializes console I/O transport state.
Definition cdc_log.cpp:246
static console_input_available_hook_t s_input_avail_hook
Definition cdc_log.cpp:33
void error_log_clear(void)
Clears error-log ring buffer state.
Definition cdc_log.cpp:105
void log_raw(const char *fmt,...)
Writes untagged raw formatted text to console.
Definition cdc_log.cpp:209
void log_hex(const char *tag, const char *label, const uint8_t *data, size_t len)
Logs binary buffer as grouped hexadecimal bytes.
Definition cdc_log.cpp:225
static size_t s_error_log_count
Definition cdc_log.cpp:49
static console_input_getchar_hook_t s_input_getchar_hook
Definition cdc_log.cpp:35
CDC Log: logging over TinyUSB CDC and UART.
int(* console_input_getchar_hook_t)(void)
Input getchar hook used to fetch one character from an additional source.
Definition cdc_log.h:109
bool(* log_authgate_hook_t)(void)
Hook polled before INFO/DEBUG/VERBOSE log lines reach the console.
Definition cdc_log.h:132
void(* console_output_hook_t)(const char *data, size_t len)
Output hook called for every console output.
Definition cdc_log.h:97
bool(* console_input_available_hook_t)(void)
Input-available hook used to check if additional input is available.
Definition cdc_log.h:103
#define ERROR_LOG_MAX_ENTRIES
Definition cdc_log.h:37
#define ERROR_LOG_LINE_LEN
Definition cdc_log.h:38