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

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;
#endif

	va_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_SANDBOX
	if (!gd) {
		os_puts(s);
		return;
	}
#endif

#ifdef CONFIG_SILENT_CONSOLE
	if (gd->flags & GD_FLG_SILENT)
		return;
#endif

#ifdef CONFIG_DISABLE_CONSOLE
	if (gd->flags & GD_FLG_DISABLE_CONSOLE)
		return;
#endif

	if (!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++)]);
}
#else
static 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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值