串口驱动
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串口设备相关的硬件操作函数。
以上是关于串口驱动的主要内容,如果未能解决你的问题,请参考以下文章