Line Disciplines

在Linux tty子系统中,Line Disciplines处在底层驱动与上层应用的之间,先来看一个例子:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/tty.h>
#include <linux/tty_ldisc.h>

static int my_ldisc_tty_open(struct tty_struct *tty)
{
	printk("%s\n", __func__);
	
	if (tty->ops->write == NULL)
		return -EOPNOTSUPP;

	tty->disc_data = NULL;
	tty->receive_room = 65536;
	
	tty_driver_flush_buffer(tty);

	return 0;
}

static void my_ldisc_tty_close(struct tty_struct *tty)
{
	printk("%s\n", __func__);
}

static ssize_t my_ldisc_tty_read(struct tty_struct *tty, struct file *file,
		unsigned char __user *buf, size_t nr)
{
	printk("%s\n", __func__);
	return 0;
}

static ssize_t my_ldisc_tty_write(struct tty_struct *tty, struct file *flie,
		const unsigned char *buf, size_t nr)
{
	printk("%s %s\n", __func__, buf);

	int space = tty_write_room(tty);
	if (space >= nr)
		return tty->ops->write(tty, buf, nr);
	set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
	return -ENOBUFS;
}

static void my_ldisc_tty_receive(struct tty_struct *tty, const u8 *data,
		char *flags, int count)
{
	int i;
	printk("%s\n", __func__);
	for (i = 0; i < count; i++)
		printk("%02x ", *data++);
	printk("\n");
}

static struct tty_ldisc_ops my_ldisc_ops = {
	.magic		= TTY_LDISC_MAGIC,
	.owner		= THIS_MODULE,
	.name		= "my_ldisc",
	.open		= my_ldisc_tty_open,
	.close		= my_ldisc_tty_close,
	.read		= my_ldisc_tty_read,
	.write		= my_ldisc_tty_write,
	.receive_buf	= my_ldisc_tty_receive,
};

static int __init my_ldisc_init(void)
{
	printk("%s\n", __func__);
	return tty_register_ldisc(N_MYLDISC, &my_ldisc_ops);
}

static void __exit my_ldisc_exit(void)
{
	printk("%s\n", __func__);
	tty_unregister_ldisc(N_MYLDISC);
}

module_init(my_ldisc_init);
module_exit(my_ldisc_exit);

MODULE_LICENSE("GPL");
注册一个ldisc驱动使用tty_register_ldisc()函数,注销使用tty_unregister_ldisc()函数,tty_register_ldisc()函数需要两个参数,第一个参数是一个数字编号,在tty.h中定义,如果你新增了一个ldisc驱动的话,需要定义一个数字编号。在串口应用用,如果你没有指定一个ldisc的话,默认使用的是N_TTY这个ldisc。另一个参数是操作接口,那么我们只需要实现这个接口中的一些函数就行了。

常用的接口函数有,open、close、read、write、receive_buf等。

在串口应用中,调用write系统调用时将触发这里的write接口,在write接口函数中,调用tty驱动的write函数将数据发送的串口中,这样串口数据就通过tx线缆发送出去了。可以看出ldisc相当于一个中转站,起到一个承上启下的作用。

如果串口电路收到数据,将触发这里的receive_buf接口,这里呢并没有做任何处理,只是简单将收到的数据给打印出来了。

ldisc驱动用在什么地方呢?通常用在在驱动层需要操作串口的地方,例如蓝牙的hci_ldisc.c、GSM的n_gsm.c都是这一类应用。


还有一个问题,在驱动层我们实际上是不知道我们到底操作的那个串口,这需要在串口应用层指定。代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <termios.h>

#define DEV_NAME	"/dev/s3c2410_serial0"

void tty_config(int fd)
{
	struct termios options;
	
	tcgetattr(fd, &options);
	
	cfsetispeed(&options, B115200);
	cfsetospeed(&options, B115200);
	
	options.c_cflag |= (CLOCAL | CREAD);
	
	options.c_cflag &= ~PARENB;
	options.c_cflag &= ~CSTOPB;
	options.c_cflag &= ~CSIZE;
	options.c_cflag |= CS8;
	
	options.c_cflag &= ~CRTSCTS;
	
	options.c_iflag &= ~(IXON | IXOFF | IXANY);
	
	tcsetattr(fd, TCSANOW, &options);
}

int main(void)
{
	int i, fd;
	char *tmp = "ldisc test\n";
	
	fd = open(DEV_NAME, O_RDWR | O_NOCTTY);
	if (fd < 0) {
		printf("open tty device error\n");
		exit(EXIT_FAILURE);
	}
	
	tty_config(fd);
	
	i = 20;
	if (ioctl(fd, TIOCSETD, &i)) {
		printf("set line discipline error\n");
		exit(EXIT_FAILURE);
	}

	for (i = 0; i < 10; i++)
		write(fd, tmp, strlen(tmp));

	while (1);
	
	close(fd);
	
	exit(EXIT_SUCCESS);
}
在串口应用中,打开一个tty设备之后,使用ioctl来指定到底用哪一个ldisc,注意编号需要和底层驱动相对应。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章