微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > Linux下的串口总线驱动(三)

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

时间:11-22 来源:互联网 点击:
五.线路规程内核代码

底层的物理驱动程序和tty驱动程序负责从硬件上收发数据,而线路规程则负责处理这些数据,并在用户空间和内核空间知觉传递数据。打开串行端口时系统默认的线路规程是N_TTY,它实现终端I/O处理。线路规程也实现通过串行传输协议实现的网络接口,PPP(N_PPP),SLIP(串行线路网际协议)(N_SLIP),红外数据(N_IRDA),蓝牙主机控制接口(N_HCI)。

我们在TTY层uart_register_driver函数里初始化termios的时候用到tty_std_termios,这个是线路的原始设置,具体定义如下

struct ktermios tty_std_termios = {

.c_iflag = ICRNL | IXON, //输入标志

.c_oflag = OPOST | ONLCR, //输出标志

.c_cflag = B38400 | CS8 | CREAD | HUPCL, //控制标志

.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |

ECHOCTL | ECHOKE | IEXTEN, //本地标志

.c_cc = INIT_C_CC, //字符控制

.c_ispeed = 38400, //输入速率

.c_ospeed = 38400 //输出速率

};

如果需要对线路原始设置的部分加以修改,则可以添加其他操作。主要分为内核空间修改线路规程和用户空间修改线路规程两个途径。内核空间修改线路规程很简单,只需要对需要修改项进行重新赋值就行了,对于用户空间修改线路规程我们来讲解下。

假如用户空间程序打开和触摸控制器相连的串行端口时,N_TCH将被绑定到底层的串行驱动程序,但假如你想编写程序清空触摸控制器接收的所有原始数据而不处理它,那你就需要修改线路规程为N_TTY并清空所有接收的数据的程序。用户空间修改线程代码如下

fd=open(“/dev/ttys0”,O_RDONLY|O_NOCTTY);

ldisc=N_TTY;

ioctl(fd,TIOCSETD,&ldisc);

好了,前面我们从应用角度分析了线路规程的设置,现在我们从理论角度,深度剖析下线路规程是怎么实现的吧。

在TTY层我们讲过TTY层的uart_register_driver和uart_register_port最终调用线路规程的tty_register_driver和tty_register_device。而tty_register_driver和tty_register_device的实现在线路规程中tty_io.c中实现的,我们可以打开tty_io.c这个文件。

首先我们看tty_init函数,在tty_init函数中执行了cdev_init(&tty_cdev, &tty_fops)一行代码,说明向内核中添加了一个cdev设备,我们跟踪tty_fops。

static const struct file_operations tty_fops = {

.llseek = no_llseek,

.read = tty_read,

.write = tty_write,

.poll = tty_poll,

.unlocked_ioctl = tty_ioctl,

.compat_ioctl = tty_compat_ioctl,

.open = tty_open,

.release = tty_release,

.fasync = tty_fasync,

};

这个结构体我们很熟悉,在字符设备中,我们就是使用的这个结构体吧。那说明我们用户进行open,read,write,ioctl等对串口操作时,第一步调用就是这里的open,read,write,ioctl。那么我们就看看怎么由这里的open,read,write,ioctl跟TTY层,UART层的open,read,write,ioctl相联系的。

我们就来看看这个open吧

static int __tty_open(struct inode *inode, struct file *filp)

{

struct tty_struct *tty = NULL;

int noctty, retval;

struct tty_driver *driver;

int index;

dev_t device = inode->i_rdev; //获取目标设备的设备号

unsigned saved_flags = filp->f_flags;

nonseekable_open(inode, filp);

retry_open:

noctty = filp->f_flags & O_NOCTTY;

index = -1;

retval = 0;

mutex_lock(&tty_mutex);

if (device == MKDEV(TTYAUX_MAJOR, 0)) { //当前进程的控制终端,/dev/tty

tty = get_current_tty();

if (!tty) { //该进程还没有控制终端

mutex_unlock(&tty_mutex);

return -ENXIO;

}

driver = tty_driver_kref_get(tty->driver); //如果打开的确实是控制终端的处理

index = tty->index;

filp->f_flags |= O_NONBLOCK;

tty_kref_put(tty);

goto got_driver;

}

#ifdef CONFIG_VT

if (device == MKDEV(TTY_MAJOR, 0)) { //当前虚拟控制台,/dev/tty0

extern struct tty_driver *console_driver;

driver = tty_driver_kref_get(console_driver);

index = fg_console; // fg_console表示当前的前台控制台

noctty = 1; //因为虚拟控制台原来就打开,故置位

goto got_driver;

}

#endif

if (device == MKDEV(TTYAUX_MAJOR, 1)) { //用于外接的控制台,/dev/console

struct tty_driver *console_driver = console_device(&index);

if (console_driver) {

driver = tty_driver_kref_get(console_driver);

if (driver) {

filp->f_flags |= O_NONBLOCK;

noctty = 1;

goto got_driver;

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top