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