Linux TTY设备驱动程序原理

如何写TTY设备驱动程序可以参考Linux Device Driver的第18章和例子程序tiny_tty.c,这里主要分析驱动程序调用的kernel API后面的细节.

驱动程序主要做3件事:

struct tty_driver *alloc_tty_driver(int lines)

int tty_register_driver(struct tty_driver *driver)

struct device *tty_register_device(struct tty_driver *driver, unsigned index,struct device *device)

其中最重要的就是tty_register_driver,它会注册一组函数指针:

struct tty_operations {

       int  (*open)(struct tty_struct * tty, struct file * filp);

       void (*close)(struct tty_struct * tty, struct file * filp);

       int  (*write)(struct tty_struct * tty,

                    const unsigned char *buf, int count);

       void (*put_char)(struct tty_struct *tty, unsigned char ch);

       void (*flush_chars)(struct tty_struct *tty);

       int  (*write_room)(struct tty_struct *tty);

       int  (*chars_in_buffer)(struct tty_struct *tty);

       int  (*ioctl)(struct tty_struct *tty, struct file * file,

                  unsigned int cmd, unsigned long arg);

       long (*compat_ioctl)(struct tty_struct *tty, struct file * file,

                          unsigned int cmd, unsigned long arg);

       void (*set_termios)(struct tty_struct *tty, struct ktermios * old);

       void (*throttle)(struct tty_struct * tty);

       void (*unthrottle)(struct tty_struct * tty);

       void (*stop)(struct tty_struct *tty);

       void (*start)(struct tty_struct *tty);

       void (*hangup)(struct tty_struct *tty);

       void (*break_ctl)(struct tty_struct *tty, int state);

       void (*flush_buffer)(struct tty_struct *tty);

       void (*set_ldisc)(struct tty_struct *tty);

       void (*wait_until_sent)(struct tty_struct *tty, int timeout);

       void (*send_xchar)(struct tty_struct *tty, char ch);

       int (*read_proc)(char *page, char **start, off_t off,

                       int count, int *eof, void *data);

       int (*write_proc)(struct file *file, const char __user *buffer,

                       unsigned long count, void *data);

       int (*tiocmget)(struct tty_struct *tty, struct file *file);

       int (*tiocmset)(struct tty_struct *tty, struct file *file,

                     unsigned int set, unsigned int clear);

};

 

tty_register_driver分析

根据驱动程序里面提供的majorminor,注册一个字符设备:

       if (!driver->major) {

              error = alloc_chrdev_region(&dev, driver->minor_start, driver->num,

                                          driver->name);

              if (!error) {

                     driver->major = MAJOR(dev);

                     driver->minor_start = MINOR(dev);

              }

       } else {

              dev = MKDEV(driver->major, driver->minor_start);

              error = register_chrdev_region(dev, driver->num, driver->name);

       }

       cdev_init(&driver->cdev, &tty_fops);

       driver->cdev.owner = driver->owner;

       error = cdev_add(&driver->cdev, dev, driver->num);

该字符设备的struct file_operations结构是:

static const struct file_operations tty_fops = {

       .llseek            = no_llseek,

       .read              = tty_read,

       .write             = tty_write,

       .poll        = tty_poll,

       .ioctl              = tty_ioctl,

       .compat_ioctl  = tty_compat_ioctl,

       .open             = tty_open,

       .release    = tty_release,

       .fasync           = tty_fasync,

};

 

当我们打开串口设备的时候,tty_open会被调用:

1 init_dev(driver, index, &tty): 该函数初始化tty_struct结构,ttyline discpline被设置为N_TTY.(在打开TTY的时候: tty_open->init_dev->initialize_tty_struct:      tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));)

2 tty->driver->open(tty, filp);

 

TTY设备的读写

tty_write:

获得line discipline,并调用do_tty_write, do_tty_write中会调用ld->write来进行实际的写操作.

       ld = tty_ldisc_ref_wait(tty);         

       if (!ld->write)

              ret = -EIO;

       else

              ret = do_tty_write(ld->write, tty, file, buf, count);

对于N_TTY,line discipline在系统启动中被定义为:(console_init->tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY))

struct tty_ldisc tty_ldisc_N_TTY = {

       .magic           = TTY_LDISC_MAGIC,

       .name            = "n_tty",

       .open            = n_tty_open,

       .close           = n_tty_close,

       .flush_buffer    = n_tty_flush_buffer,

       .chars_in_buffer = n_tty_chars_in_buffer,

       .read            = read_chan,

       .write           = write_chan,

       .ioctl           = n_tty_ioctl,

       .set_termios     = n_tty_set_termios,

       .poll            = normal_poll,

       .receive_buf     = n_tty_receive_buf,

       .write_wakeup    = n_tty_write_wakeup

};

因此写操作就是调用write_chan, 而它会调用驱动程序中注册的函数tty->driver->write(tty, b, nr).

 

对于读操作tty_read,类似于写操作,会调用到read_chan,read_chan会等待驱动程序往上传来的数据.驱动程序会调用

tty_insert_flip_char, tty_insert_flip_string tty_flip_buffer_push等等往上传递数据,最终会调用到flush_to_ldisc, flush_to_ldisc会调用disc->receive_bufline discipline传递数据,对于N_TTY,就是调用n_tty_receive_buf, n_tty_receive_buf在收到数据后,会唤醒read_chan, read_chan就可以读到数据了.

总之,数据的读写都是通过line discipline进行的。

改变line discipline

我们也可以通过IO control的方式改变TTYline discipline:

tty_ioctl(TIOCSETD)->tiocsetd(tty, p)->tty_set_ldisc: 这个函数里面会调用old line disciplineclose函数还有new line disciplineopen函数.当然前提是新的line discipline是已经存在了的, line discipline是通过tty_register_ldisc注册到系统中的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值