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,注意編號需要和底層驅動相對應。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章