Cubietruck开发板SPL阶段的printf重定向问题

来源:互联网 发布:信贷员靠谱抢单软件 编辑:程序博客网 时间:2024/05/17 09:36
int printf(const char *fmt, ...){va_list args;uint i;char printbuffer[CONFIG_SYS_PBSIZE];#if !defined(CONFIG_SANDBOX) && !defined(CONFIG_PRE_CONSOLE_BUFFER)if (!gd->have_console)return 0;#endifva_start(args, fmt);/* For this to work, printbuffer must be larger than * anything we ever want to print. */i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args);va_end(args);/* Print the string */puts(printbuffer);return i;}

这里的va_list、va_start、va_end就不用说了,具体参考http://blog.csdn.net/u013691997/article/details/23426619

#if !defined(CONFIG_SANDBOX) && !defined(CONFIG_PRE_CONSOLE_BUFFER)
if (!gd->have_console)
return 0;
#endif

意思是加入没有defineCONFIG_SANDBOX和CONFIG_PRE_CONSOLE_BUFFER

那么则检查gd->have_console,也就是检查是否存在串口,如果不存在串口就要退出,

为什么要说CONFIG_PRE_CONSOLE_BUFFER呢?上面的话反过来就是,如果存在缓冲,

就不用检查是否有串口。再往下看,调用了puts()

void puts(const char *s){#ifdef CONFIG_SANDBOXif (!gd) {os_puts(s);return;}#endif#ifdef CONFIG_SILENT_CONSOLEif (gd->flags & GD_FLG_SILENT)return;#endif#ifdef CONFIG_DISABLE_CONSOLEif (gd->flags & GD_FLG_DISABLE_CONSOLE)return;#endifif (!gd->have_console)return pre_console_puts(s);if (gd->flags & GD_FLG_DEVINIT) {/* Send to the standard output */fputs(stdout, s);} else {/* Send directly to the handler */serial_puts(s);}}
分析可知puts检查have_console、gd->flags & GD_FLG_DEVINIT,也就是

检查是否有串口、如果有是否初始化,注意这里的初始化不同于一般意义上的初始化

搜索项目可知,gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */

是在console_init_r中执行的,而SPL阶段根本不会调用console_init_r

所以puts检查是否有串口,没串口就调用pre_console_puts(s);有串口就调用

serial_puts(s);那么分析一下pre_console_puts

#ifdef CONFIG_PRE_CONSOLE_BUFFER#define CIRC_BUF_IDX(idx) ((idx) % (unsigned long)CONFIG_PRE_CON_BUF_SZ)static void pre_console_putc(const char c){char *buffer = (char *)CONFIG_PRE_CON_BUF_ADDR;buffer[CIRC_BUF_IDX(gd->precon_buf_idx++)] = c;}static void pre_console_puts(const char *s){while (*s)pre_console_putc(*s++);}static void print_pre_console_buffer(void){unsigned long i = 0;char *buffer = (char *)CONFIG_PRE_CON_BUF_ADDR;if (gd->precon_buf_idx > CONFIG_PRE_CON_BUF_SZ)i = gd->precon_buf_idx - CONFIG_PRE_CON_BUF_SZ;while (i < gd->precon_buf_idx)putc(buffer[CIRC_BUF_IDX(i++)]);}#elsestatic inline void pre_console_putc(const char c) {}static inline void pre_console_puts(const char *s) {}static inline void print_pre_console_buffer(void) {}#endif
可见,如果定义了缓冲puts将字符串缓冲起来,没有定义缓冲的话什么也不做,

小结一下:

printf执行有三种结果,1.什么都不做只return 0.2.puts字符串到缓冲

3.puts字符串到设备(串口)。

值得注意的是,printf调用puts的话要么输出到缓冲要么输出到设备,而单看puts

时,其有可能什么都不做。如果printf输出到缓冲,然后什么时间处理缓冲呢??

那么回过头来分一下整个preloader_console_init(),只有在get_current()的时候

可能会输出到缓冲(初始化设备失败),而get_current()成功的话,其本身不会

puts错误信息,而之后gd->have_console = 1;所以再调用printf或者puts的话会

直接输出到设备。

/** * serial_puts() - Output string via currently selected serial port * @s:Zero-terminated string to be output from the serial port. * * This function outputs a zero-terminated string via currently * selected serial port. This function behaves as an accelerator * in case the hardware can queue multiple characters for transfer. * The whole string that is to be output is available to the function * implementing the hardware manipulation. Transmitting the whole * string may take some time, thus this function may block for some * amount of time. This function uses the get_current() call to * determine which port is selected. */void serial_puts(const char *s){get_current()->puts(s);}
继续一层一层剥开就是

void_serial_putc(const char c,const int port){if (c == '\n')NS16550_putc(PORT, '\r');NS16550_putc(PORT, c);}

NS16550的问题就不说了。

一层一层的这么多,这里只想说,printf是一个中间函数,对于它的重定向其实就是

给它一个可用的叶子函数,另外每个printf函数的具体过程都不一样,所以在重定向的

方法也不一样,这里跟stm32(keil使用的arm编译器)比较一下就看出来了


再回忆一下a20的uart串口驱动如何与ns16550挂钩的:

preloader_console_init()——>serial_init()——>get_current——>default_serial_console()

preloader_console_init()——>serial_init():由board.c调用spl.c

serial_init()——>get_current:由spl.c调用drivers/serial/serial.c

get_current——>default_serial_console():

default_serial_console()在serial.h中声明,而serial目录下的很多文件都包含这个serial.h

并且实现了default_serial_console()函数,其他目录下也有此函数的实现,所以与

serial_ns16550.c挂钩是通过makefile实现的,另外还要注意为什么攒在serial_ns16550.c

而不是直接ns16550.c


再次编辑添加:貌似再SPL阶段只有printf,而没有scanf。

0 0