本文共 2870 字,大约阅读时间需要 9 分钟。
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.cvoid 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, ······};
在Linux驱动代码中,实行代码和数据分离的思想,将外部设备的信息(基地址、中断号等)另外封装,不放到驱动代码中,通过将封装后的信息以参数形式传递给驱动代码。与外部设备具体信息相关的初始化我们一般放在probe函数中进行。
在中注册了一个平台驱动serial_imx_driver,串口设备会以平台设备的形式注册到平台总线,当串口设备和这个平台驱动匹配以后就会执行 serial_imx_driver.probe函数。
//imx.cstatic 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);}
总结一下《串口驱动(一)》和《串口驱动(二)》两部分做了哪些初始化。
如上图所示,前面的初始化部分,主要就是对以上的四个操作函数集进行填充、注册。
现在串口驱动的整体框架也就出来了,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串口设备相关的硬件操作函数。
转载地址:http://lhuen.baihongyu.com/