00:05: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A

1.参数解析

early_param(“earlycon”, param_setup_earlycon);

#define early_param(str, fn) \ __setup_param(str, fn, fn, 1)

#define __setup_param(str, unique_id, fn, early)			\
	static const char __setup_str_##unique_id[] __initconst		\
		__aligned(1) = str; 					\
	static struct obs_kernel_param __setup_##unique_id		\
		__used __section(.init.setup)				\
		__attribute__((aligned((sizeof(long)))))		\
		= { __setup_str_##unique_id, fn, early }

1,所有的系统启动参数都是由形如 static int __init foo(char *str);的函数来支持的
注:#define __init attribute ((section (".init.text")))
申明所有的启动参数支持函数都放入.init.text段

struct obs_kernel_param {
	const char *str;
	int (*setup_func)(char *);
	int early;
};
static const char  __setup_str_param_setup_earlycon  __initconst __aligned(1)  = earlycon
static struct obs_kernel_param  __setup_param_setup_earlycon __used __section(.init.setup)	
__attribute__((aligned((sizeof(long))))) = {
	__setup_str_param_setup_earlycon,
	param_setup_earlycon,
	0
}

也就是说,启动参数(函数指针)被封装到obs_kernel_param结构中,
所有的内核启动参数形成内核映像.init.setup段中的一个
obs_kernel_param数组

__setup与early_param不同的是,early_param 宏注册的内核选项必须要在其他内核选项之前被处理。

在函数start_kernel中,parse_early_param处理early_param定义的参数,parse_args处理__setup定义的参数

内核对启动参数的解析:下面函数历遍obs_kernel_param数组,调用
支持函数

asmlinkage __visible void __init start_kernel(void)—>
parse_early_param();—>
parse_early_options(tmp_cmdline);—>
parse_args(“early options”, cmdline, NULL, 0, 0, 0, NULL, do_early_param);---->
do_early_param------>
循环遍历自己setup中填充的函数

2 函数的调用

经过kernel的层层的调用最后回到了 match->setup(&early_console_dev,buf)
这个就调用我们注册的setup函数。

/tty/serial/8250/8250_uniphier.c:61:OF_EARLYCON_DECLARE(uniphier, "socionext,uniphier-uart",
./tty/serial/imx.c:2122:OF_EARLYCON_DECLARE(ec_imx6q, "fsl,imx6q-uart", imx_console_early_setup);
./tty/serial/imx.c:2123:OF_EARLYCON_DECLARE(ec_imx21, "fsl,imx21-uart", imx_console_early_setup);
./tty/serial/altera_uart.c:532:OF_EARLYCON_DECLARE(uart, "altr,uart-1.0", altera_uart_earlycon_setup);
./tty/serial/omap-serial.c:1282:OF_EARLYCON_DECLARE(omapserial, "ti,omap2-uart", early_omap_serial_setup);
./tty/serial/omap-serial.c:1283:OF_EARLYCON_DECLARE(omapserial, "ti,omap3-uart", early_omap_serial_setup);
./tty/serial/omap-serial.c:1284:OF_EARLYCON_DECLARE(omapserial, "ti,omap4-uart", early_omap_serial_setup);
./tty/serial/meson_uart.c:567:OF_EARLYCON_DECLARE(meson, "amlogic,meson-uart",
./tty/serial/meson_uart.c:570:OF_EARLYCON_DECLARE(meson, "amlogic,meson-ao-uart",
./tty/serial/sh-sci.c:3461:OF_EARLYCON_DECLARE(sci, "renesas,sci", sci_early_console_setup);
./tty/serial/sh-sci.c:3462:OF_EARLYCON_DECLARE(scif, "renesas,scif", scif_early_console_setup);
./tty/serial/sh-sci.c:3463:OF_EARLYCON_DECLARE(scifa, "renesas,scifa", scifa_early_console_setup);
./tty/serial/sh-sci.c:3464:OF_EARLYCON_DECLARE(scifb, "renesas,scifb", scifb_early_console_setup);
./tty/serial/sh-sci.c:3465:OF_EARLYCON_DECLARE(hscif, "renesas,hscif", hscif_early_console_setup);
./tty/serial/samsung.c:2512:OF_EARLYCON_DECLARE(s3c2410, "samsung,s3c2410-uart",
./tty/serial/samsung.c:2526:OF_EARLYCON_DECLARE(s3c2412, "samsung,s3c2412-uart",
./tty/serial/samsung.c:2528:OF_EARLYCON_DECLARE(s3c2440, "samsung,s3c2440-uart",
./tty/serial/samsung.c:2530:OF_EARLYCON_DECLARE(s3c6400, "samsung,s3c6400-uart",
./tty/serial/samsung.c:2544:OF_EARLYCON_DECLARE(s5pv210, "samsung,s5pv210-uart",
./tty/serial/samsung.c:2546:OF_EARLYCON_DECLARE(exynos4210, "samsung,exynos4210-uart",
./tty/serial/mps2-uart.c:477:OF_EARLYCON_DECLARE(mps2, "arm,mps2-uart", mps2_early_console_setup);
./tty/serial/xilinx_uartps.c:1168:OF_EARLYCON_DECLARE(cdns, "xlnx,xuartps", cdns_early_console_setup);
./tty/serial/xilinx_uartps.c:1169:OF_EARLYCON_DECLARE(cdns, "cdns,uart-r1p8", cdns_early_console_setup);
./tty/serial/xilinx_uartps.c:1170:OF_EARLYCON_DECLARE(cdns, "cdns,uart-r1p12", cdns_early_console_setup);
./tty/serial/xilinx_uartps.c:1171:OF_EARLYCON_DECLARE(cdns, "xlnx,zynqmp-uart", cdns_early_console_setup);
./tty/serial/amba-pl011.c:2471:OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup);
./tty/serial/amba-pl011.c:2472:OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", pl011_early_console_setup);
			
#define _OF_EARLYCON_DECLARE(_name, compat, fn, unique_id)              \
        static const struct earlycon_id unique_id                       \
             EARLYCON_USED_OR_UNUSED __initconst                        \
                = { .name = __stringify(_name),                         \
                    .compatible = compat,                               \
                    .setup = fn  };                                     \
        static const struct earlycon_id EARLYCON_USED_OR_UNUSED         \
                __section(__earlycon_table)                             \
                * const __PASTE(__p, unique_id) = &unique_id

#define OF_EARLYCON_DECLARE(_name, compat, fn)                          \
        _OF_EARLYCON_DECLARE(_name, compat, fn,                         \
                             __UNIQUE_ID(__earlycon_##_name))

#define EARLYCON_DECLARE(_name, fn)     OF_EARLYCON_DECLARE(_name, "", fn)

3关于串口的几个重要知识点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
uart_port 是关于硬件部分的填充,同时关注uart_add_one_port这个函数的功能,一个tty 可以对应多个uart_port

4 回到最开始的问题

log的出处:kernel/driver/tty/serial/serial_core.c
本章的主要目的是找到log的出处,然后确定io类型跟io地址的由来,来源bios经过设备扫描后,全部进行映射,通过acpi table传给os。
我们的驱动程序加载了两个驱动设备 :

Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
一个pnp bus上的8250 一个是自己创造的16550设备:
先看16550:
serial8250_isa_devs = platform_device_alloc(“serial8250”,
PLAT8250_DEV_LEGACY);
ret = platform_device_add(serial8250_isa_devs);
serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
ret = platform_driver_register(&serial8250_isa_driver);

serial8250_init---->
serial8250_isa_init_ports();—>
serial8250_init_port(up);—>

for (i = 0, up = serial8250_ports;
	     i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
	     i++, up++) {
		struct uart_port *port = &up->port;

		port->iobase   = old_serial_port[i].port;
		port->irq      = irq_canonicalize(old_serial_port[i].irq);
		port->irqflags = 0;
		port->uartclk  = old_serial_port[i].baud_base * 16;
		port->flags    = old_serial_port[i].flags;
		port->hub6     = 0;
		port->membase  = old_serial_port[i].iomem_base;
		port->iotype   = old_serial_port[i].io_type;
		port->regshift = old_serial_port[i].iomem_reg_shift;
		serial8250_set_defaults(up);

		port->irqflags |= irqflag;
		if (serial8250_isa_config != NULL)
			serial8250_isa_config(i, &up->port, &up->capabilities);
	}

static const struct old_serial_port old_serial_port[] = {
	SERIAL_PORT_DFNS /* defined in asm/serial.h */
};

#define SERIAL_PORT_DFNS								\
	/* UART		CLK		PORT	IRQ	FLAGS			    */	\
	{ .uart = 0,	BASE_BAUD,	0x3F8,	4,	STD_COMX_FLAGS	}, /* ttyS0 */	\
	{ .uart = 0,	BASE_BAUD,	0x2F8,	3,	STD_COMX_FLAGS	}, /* ttyS1 */	\
	{ .uart = 0,	BASE_BAUD,	0x3E8,	4,	STD_COMX_FLAGS	}, /* ttyS2 */	\
	{ .uart = 0,	BASE_BAUD,	0x2E8,	3,	STD_COM4_FLAGS	}, /* ttyS3 */

#endif /* _ASM_X86_SERIAL_H */

然后在pnp bus上面的这个:
首先这个pnp bus节点之前没有遇到过,看了大概猜测是从acpi table进行匹配,获取设备的私有函数。
serial_pnp_probe—>

if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) {
	uart.port.iobase = pnp_port_start(dev, 2);--->
	uart.port.iotype = UPIO_PORT;

struct resource *res = pnp_get_resource(dev, IORESOURCE_IO, bar);—>

	list_for_each_entry(pnp_res, &dev->resources, list) {
		res = &pnp_res->res;
		if (pnp_resource_type(res) == type && num-- == 0)---->
			return res;
	}

从上面函数的接口看到获取端口资源,如果是内存型的,就是获取内存资源,设备资源是怎么加载进去的,和内核是怎么遍历pcie设备树,并重新分配映射地址的 留到下一个问题探索中。

最后猜测:从最后的资源获取中,可以感觉到最上面的log打印出的信息,是通过pcie table中获取的。
后面发现port不是这个,有一个pcie串口驱动,把那个port加载到tty路径中。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: c 是一个赋值语句。它用于将一个值赋给一个变量。例如: c := 10 在这个例子中,变量 c 的值被设置为 10。 注意,赋值语句的等号两边不能有空格。正确的写法是 c:=10,而不是 c := 10。 此外,赋值语句中的变量必须先声明,然后才能使用。例如,在 Go 语言中,可以使用 var 关键字来声明变量: var c int c = 10 在这个例子中,变量 c 被声明为 int 类型,并被赋值为 10。 ### 回答2: 在编程中,"c :=" 表示对变量c进行赋值操作。这是一种短变量声明的语法,用于定义一个新的变量c,并将其初始化为一个值。这种简洁的写法可以在不显式声明变量类型的情况下进行变量赋值。 在短变量声明中,变量类型会根据赋值表达式的类型自动推导出来。这种方式适用于我们已经知道赋值表达式的类型的情况,或者在短小的代码段中使用。 例如,我们可以使用短变量声明来定义一个整数变量,并将其初始化为一个值: c := 10 这样,变量c就会被声明为一个整数类型,并且被赋值为10。 短变量声明还可以用于同时定义多个变量,只需要在赋值表达式中使用逗号分隔。例如: a, b := 10, 20 这样就定义了两个整数变量a和b,并分别将它们赋值为10和20。 需要注意的是,短变量声明只能在函数内部使用,不能用于全局变量的声明。 总之,"c :=" 表示对变量c进行赋值操作,并且根据赋值表达式的类型自动推导出变量的类型。这种简洁的写法方便了我们在编程中进行变量赋值的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值