#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,注意編號需要和底層驅動相對應。