您的位置 首页 知识

Linux下的串口总线驱动(一)

一.系统理论PC机南桥的LPC总线(LowPinCount并行总线,代替以前的ISA总线)上挂接了一个超级IO模块,而UART是这个超级模块芯片组的一

一.体系理论

PC机南桥的LPC总线(Low Pin Count并行总线,替代曾经的ISA总线)上挂接了一个超级I/O模块,而UART是这个超级模块芯片组的一部分,这个UART经过RS232线程转化与串行端口相连。与RS232不同,RS485并不是规范的PC接口,但在嵌入式范畴,会为了牢靠通讯而运用RS485,RS485运用差分信号,因而其传输间隔能够抵达数百米,而RS232传输间隔仅数几米,在处理器一端,RS485接口是半双工的UART操作。

Linux包含如下几种终端设备:串行端口终端(/dev/ttySn)、伪终端(/dev/pty)、操控终端(/dev/tty)、操控台终端(/dev/ttyn,/dev/conslole)。串行端口终端运用的设备名为/dev/ttyS0,/dev/ttyS1等,对应的设备号为(4,0),(4,1)。经过检查/proc/tty/drivers文件能够知道什么类型的tty设备存在以及什么驱动被加载到内核,这个文件包含一个当时存在的不同tty驱动的列表,包含驱动名,缺省的节点名,驱动的主编号,驱动的次编号规模,以及tty驱动的类型。

I/O体系调用是从带有线路规程的TTY I/O中心开端,然后经过TTY层,终究抵达UART驱动层。首要触及串口内核装备、UART层内核代码、TTY层内核代码、线路规程内核代码、串口测验代码五个部分。

二.串口内核装备

关于Mini2440串口驱动,我想从装备开端讲起。在内核中Kconfig有必要完结一层层调用,假如没有在上一个Kconfig中调用该层Kconfig,那么该层Kconfig中的内容不会在此呈现。这种状况下,只有当该层的Kconfig被其他层调用,该层Kconfig中的内容才会被显现。所以咱们找找drivers/serial/Kconfig在哪里被调用的呢?

在/drivers/char/kconfig中能够看到一行代码source “drivers/serial/Kconfig”,那咱们就到drivers/serial/Kconfig下看看

Samsung SoC serial support对应于samsung.o serial_core.o

config SERIAL_SAMSUNG

tristate “Samsung SoC serial support”

depends on ARM && PLAT_S3C

select SERIAL_CORE

Support for console on Samsung SoC serial port对应于操控台驱动

Samsung S3C2440/S3C2442 Serial port support对应于s3c2440.o

在/drivers/char/Makefile中能够看到

obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o

咱们知道tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o已编入内核

自此,咱们知道关于串口驱动,咱们内核中被编译了s3c2440.o samsung.o serial_core.o tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o

咱们对此进行分类,归于UART层的是s3c2440.o samsung.o;归于TTY层的是serial_core.o;归于线路规程的是tty_io.o n_tty.o tty_ioctl.o tty_ldisc.o tty_buffer.o tty_port.o 。

好了,关于串口的地图咱们现已剖析好了,那咱们就依照UART层,TTY层,线路规程一个个的逛逛吧。

三.UART层内核代码

咱们先看看samsung.o的init代码吧,这里边完结了uart_driver的注册

static int __init s3c24xx_serial_modinit(void)

{

int ret;

ret = uart_register_driver(&s3c24xx_uart_drv); //注册uart_driver

if (ret < 0) {

printk(KERN_ERR “failed to register UART driver\n”);

return -1;

}

return 0;

}

static struct uart_driver s3c24xx_uart_drv = {

.owner = THIS_MODULE,

.dev_name = “s3c2410_serial”, //设备名

.nr = CONFIG_SERIAL_SAMSUNG_UARTS, //UART端口个数

.cons = S3C24XX_SERIAL_CONSOLE, //指向操控台结构

.driver_name = S3C24XX_SERIAL_NAME, //驱动的姓名

.major = S3C24XX_SERIAL_MAJOR, //串口主设备号

.minor = S3C24XX_SERIAL_MINOR, //串口次设备号

};

咱们重视下上面这个结构体中一个成员S3C24XX_SERIAL_CONSOLE

#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console

static struct console s3c24xx_serial_console = {

.name = S3C24XX_SERIAL_NAME,

.device = uart_console_device,

.flags = CON_PRINTBUFFER,

.index = -1,

.write = s3c24xx_serial_console_write,

.setup = s3c24xx_serial_console_setup

};

上面是操控台的结构体成员。

关于UART驱动,咱们除了需求注册uart_driver外,还需求注册端口,咱们看看s3c2440.o。

这个文件里边注册了一个渠道设备,其间渠道设备的勘探函数终究调用了samsung.o中的s3c24xx_serial_probe函数。

int s3c24xx_serial_probe(struct platform_device *dev,

struct s3c24xx_uart_info *info)

{

struct s3c24xx_uart_port *ourport;

int ret;

dbg(“s3c24xx_serial_probe(%p, %p) %d\n”, dev, info, probe_index);

ourport = &s3c24xx_serial_ports[probe_index]; //挑选s3c24xx_uart_port

probe_index++; //索引号自增

dbg(“%s: initialising port %p…\n”, __func__, ourport);

ret = s3c24xx_serial_init_port(ourport, info, dev); //初始化串口

if (ret < 0)

goto probe_err;

dbg(“%s: adding port\n”, __func__);

uart_add_one_port(&s3c24xx_uart_drv, &ourport->port); //向内核注册端口

platform_set_drvdata(dev, &ourport->port); //设置私有数据

ret = device_create_file(&dev->dev, &dev_attr_clock_source); //增加设备特点

if (ret < 0)

printk(KERN_ERR “%s: failed to add clksrc attr.\n”, __func__);

ret = s3c24xx_serial_cpufreq_register(ourport); //注册CPU频率

if (ret < 0)

dev_err(&dev->dev, “failed to add cpufreq notifier\n”);

return 0;

probe_err:

return ret;

}

经过上面的函数,咱们发现在UART层,咱们调用了uart_add_one_port函数完结端口的增加,咱们来看看增加了什么端口呢?

static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {

[0] = {

.port = {

.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),

.iotype = UPIO_MEM,

.irq = IRQ_S3CUART_RX0,

.uartclk = 0,

.fifosize = 16,

.ops = &s3c24xx_serial_ops, //对UART操作的函数

.flags = UPF_BOOT_AUTOCONF,

.line = 0,

}

},

[1] = {

.port = {

.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),

.iotype = UPIO_MEM,

.irq = IRQ_S3CUART_RX1,

.uartclk = 0,

.fifosize = 16,

.ops = &s3c24xx_serial_ops, //对UART操作的函数

.flags = UPF_BOOT_AUTOCONF,

.line = 1,

}

},

#if CONFIG_SERIAL_SAMSUNG_UARTS > 2

[2] = {

.port = {

.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),

.iotype = UPIO_MEM,

.irq = IRQ_S3CUART_RX2,

.uartclk = 0,

.fifosize = 16,

.ops = &s3c24xx_serial_ops, //对UART操作的函数

.flags = UPF_BOOT_AUTOCONF,

.line = 2,

}

},

#endif

#if CONFIG_SERIAL_SAMSUNG_UARTS > 3

[3] = {

.port = {

.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),

.iotype = UPIO_MEM,

.irq = IRQ_S3CUART_RX3,

.uartclk = 0,

.fifosize = 16,

.ops = &s3c24xx_serial_ops, //对UART操作的函数

.flags = UPF_BOOT_AUTOCONF,

.line = 3,

}

}

#endif

};

在端口的界说中,咱们知道s3c24xx_uart_port中界说了一个uart_port结构体,持续盯梢对UART的操作函数

static struct uart_ops s3c24xx_serial_ops = {

.pm = s3c24xx_serial_pm,

.tx_empty = s3c24xx_serial_tx_empty, //发送是否忙

.get_mctrl = s3c24xx_serial_get_mctrl,

.set_mctrl = s3c24xx_serial_set_mctrl,

.stop_tx = s3c24xx_serial_stop_tx,

.start_tx = s3c24xx_serial_start_tx, //类似于write

.stop_rx = s3c24xx_serial_stop_rx,

.enable_ms = s3c24xx_serial_enable_ms,

.break_ctl = s3c24xx_serial_break_ctl,

.startup = s3c24xx_serial_startup, //类似于open

.shutdown = s3c24xx_serial_shutdown, //类似于close

.set_termios = s3c24xx_serial_set_termios, //设置线路规程

.type = s3c24xx_serial_type,

.release_port = s3c24xx_serial_release_port, //开释端口资源

.request_port = s3c24xx_serial_request_port, //请求端口资源

.config_port = s3c24xx_serial_config_port, //装备端口

.verify_port = s3c24xx_serial_verify_port,

};

关于上述uart_ops函数,咱们需求自己去完结uart层的详细操作。

咱们在UART层首要触及uart_driver,uart_port,uart_ops三个结构体,并调用tty层的uart_register_driver和uart_add_one_port完结驱动和端口的注册,UART层详细操作函数需求用户自己规划。

好了,总结下UART驱动层需求完结的使命:

其一,界说uart_driver、uart_ops、uart_port等结构体的实例并在恰当的当地依据详细硬件和驱动的状况初始化它们,当然详细设备XXX的驱动能够将这些结构套在新界说的XXX_uart_driver、XXX_uart_ops、XXX_uart_port之内。

其二,在模块初始化时调用uart_register_driver()和uart_add_one_port()以注册UART驱动并增加端口,在模块卸载时调用uart_unregister_driver()和uart_remove_one_port()以刊出UART驱动并移除端口。

其三,依据详细硬件的datasheet完结uart_ops中的成员函数,这些函数的完结成为UART驱动的主体作业。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/zhishi/262310.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部