Zephyr printk实现介绍
zephyr printk的实现分为两步:
1、字符输出函数console_out()的实现
2、printk()的实现
printk()通过格式化字符串,并调用console_out()实现了打印的效果。
以下详细说明:
一、字符输出函数console_out()的实现
1、初始化uart作为log输出的物理接口。(device_get_binding初始化外设属于设备树模型,以后会写另一篇博客详细介绍,此处只要知道该API初始化了uart即可)
1 static int uart_console_init(const struct device *arg) 2 { 3 4 ARG_UNUSED(arg); 5 6 /* Claim console device */ 7 uart_console_dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME); 8 9 uart_console_hook_install(); 10 11 return 0; 12 }
2、字符发送API:console_out被注册到printk.c文件夹下的_char_out函数。(注意:_char_out最终会被printk调用)
//drivers/console/uart_console.c
1 static void uart_console_hook_install(void) 2 { 3 __stdout_hook_install(console_out); 4 __printk_hook_install(console_out); 5 }
6 //lib/os/printk.c
7 void __printk_hook_install(int (*fn)(int))
8 {
9 _char_out = fn; //_char_out = console_out
10 }
//console_out()调用串口API:uart_poll_out发送字符
static int console_out(int c) { #ifdef CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS int handled_by_debug_server = HANDLE_DEBUG_HOOK_OUT(c); if (handled_by_debug_server) { return c; } #endif /* CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS */ if ('\n' == c) { uart_poll_out(uart_console_dev, '\r'); } uart_poll_out(uart_console_dev, c); return c; }
二、printk()的实现
_char_out是发送字符的函数,被注册到char_out中了。
cbvprintf调用char_out发送字符。printk逐级调用到cbvprintf。
1 void printk(const char *fmt, ...) 2 { 3 va_list ap; 4 5 va_start(ap, fmt); 6 7 if (IS_ENABLED(CONFIG_LOG_PRINTK)) { 8 log_printk(fmt, ap); 9 } else { 10 vprintk(fmt, ap); 11 } 12 va_end(ap); 13 } 14 15 void vprintk(const char *fmt, va_list ap) 16 { 17 struct out_context ctx = { 0 }; 18 #ifdef CONFIG_PRINTK_SYNC 19 k_spinlock_key_t key = k_spin_lock(&lock); 20 #endif 21 22 cbvprintf(char_out, &ctx, fmt, ap); 23 24 #ifdef CONFIG_PRINTK_SYNC 25 k_spin_unlock(&lock, key); 26 #endif 27 } 28 29 static int char_out(int c, void *ctx_p) 30 { 31 struct out_context *ctx = ctx_p; 32 33 ctx->count++; 34 return _char_out(c); 35 }