串口驱动

Posted 四季帆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了串口驱动相关的知识,希望对你有一定的参考价值。

1. 注册tty的ldisc

        ldisc全称 line discipline(线路规程),因为历史原因,tty属于一类设备,而串口设备只是其中一种,所以该模块负责将用户操作桥接到不同的tty驱动。从代码上来看就是一个数组 tty_ldiscs[],比如串口驱动对于的线路规程是tty_ldiscs[0]。

//tty_io.c
/*
 * Initialize the console device. This is called *early*, so
 * we can't necessarily depend on lots of kernel help here.
 * Just do some early initializations, and do the complex setup
 * later.
 */
void __init console_init(void)
{
	initcall_t *call;

	/* Setup the default TTY line discipline. */
	tty_ldisc_begin();

	/*
	 * set up the console device so that later boot sequences can
	 * inform about problems etc..
	 */
	call = __con_initcall_start;
	while (call < __con_initcall_end) {
		(*call)();
		call++;
	}
}

//tty_ldisc.c
void tty_ldisc_begin(void)
{
	/* Setup the default TTY line discipline. */
	(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
}

int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
{
	tty_ldiscs[disc] = new_ldisc;   //即tty_ldiscs[N_TTY]=tty_ldisc_N_TTY
	new_ldisc->num = disc;
	new_ldisc->refcount = 0;

	return ret;
}

struct tty_ldisc_ops tty_ldisc_N_TTY = {
    .magic           = TTY_LDISC_MAGIC,
    .name            = "n_tty",
    .open            = n_tty_open,
    .close           = n_tty_close,
    .read            = n_tty_read,
    .write           = n_tty_write,
    ······
};

2. 动态初始化

        在Linux驱动代码中,实行代码和数据分离的思想,将外部设备的信息(基地址、中断号等)另外封装,不放到驱动代码中,通过将封装后的信息以参数形式传递给驱动代码。与外部设备具体信息相关的初始化我们一般放在probe函数中进行。

        在《串口驱动(一)》中注册了一个平台驱动serial_imx_driver,串口设备会以平台设备的形式注册到平台总线,当串口设备和这个平台驱动匹配以后就会执行 serial_imx_driver.probe函数。

//imx.c
static int serial_imx_probe(struct platform_device *pdev)
{
	ret = serial_imx_probe_dt(sport, pdev);
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	base = devm_ioremap_resource(&pdev->dev, res);

	sport->port.dev = &pdev->dev;
	sport->port.mapbase = res->start;
	sport->port.membase = base;
	sport->port.type = PORT_IMX,
	sport->port.iotype = UPIO_MEM;
	sport->port.irq = platform_get_irq(pdev, 0);
	sport->rxirq = platform_get_irq(pdev, 0);
	sport->txirq = platform_get_irq(pdev, 1);
	sport->rtsirq = platform_get_irq(pdev, 2);
	sport->port.fifosize = 32;
	sport->port.ops = &imx_pops;     //5.关键结构体的赋值
	sport->port.flags = UPF_BOOT_AUTOCONF;
	init_timer(&sport->timer);
	sport->timer.function = imx_timeout;
	sport->timer.data     = (unsigned long)sport;

	sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
	sport->clk_per = devm_clk_get(&pdev->dev, "per");
	sport->port.uartclk = clk_get_rate(sport->clk_per);
	
	sport->port.uartclk = clk_get_rate(sport->clk_per);
	imx_ports[sport->port.line] = sport;
	platform_set_drvdata(pdev, sport);
	return uart_add_one_port(&imx_reg, &sport->port);
}

3. 总结

        总结一下《串口驱动(一)》和《串口驱动(二)》两部分做了哪些初始化。

        如上图所示,前面的初始化部分,主要就是对以上的四个操作函数集进行填充、注册。

        现在串口驱动的整体框架也就出来了,tty_core层对于tty_io.c等文件,line discipline层对应于tty_ldisc.c等文件,serial_driver层对应serial_core.c、imx.c等文件。

        下一篇博客详细分析串口设备的打开过程,这里提前预示一下:用户打开串口设备时,会调用tty_open-->uart_open-->n_tty_open-->imx_startup,经过层层封装的调用,最终调用imx串口设备相关的硬件操作函数。

以上是关于串口驱动的主要内容,如果未能解决你的问题,请参考以下文章

串口驱动

2021-06-09 STC8单片机串口驱动函数

2021-06-09 STC8单片机串口驱动函数

2021-06-09 STC8单片机串口驱动函数

2021-06-09 STC8单片机串口驱动函数

STM8单片机串口驱动的深度解析