[tty与uart]2.tty驱动分析

Posted aaronGao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[tty与uart]2.tty驱动分析相关的知识,希望对你有一定的参考价值。

转自:http://www.wowotech.net/linux_kenrel/183.html

目录:

1 首先分析设备驱动的注册

1.1 uart_register_driver分析

1.2 tty_register_driver分析

1.3 serial8250_register_ports()函数分析

1.4 serial8250_probe()函数分析

2 然后,我们来看设备的打开过程

3 TTY设备的读

3.1 read_chan()

4 TTY设备的写

5 总结 

首先分析设备驱动的注册

对于8250.c来说,主要涉及:

  • serial8250_init()--->uart_register_driver(&serial8250_reg)
  • serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev)
  • serial8250_probe(struct platform_device *dev)

struct uart_driver serial8250_reg的定义如下:

1 static static struct uart_driver serial8250_reg = {
2         .owner                  = THIS_MODULE,
3         .driver_name            = "serial",
4         .dev_name               = "ttyS",
5         .major                  = TTY_MAJOR,
6         .minor                  = 64,
7         .nr                     = UART_NR,
8         .cons                   = SERIAL8250_CONSOLE,
9 };
struct uart_driver serial8250_reg

 

1.1 uart_register_driver分析

主要完成了一下功能:

  • 分配数个uart_state结构体内存:      (在uart_add_one_port()里会用到它来关联uart_port)
  • 分配tty_driver。normal  = alloc_tty_driver(drv->nr)
  • 关联struct uart_driver和tty_driver:
    uart_driver-> tty_driver= tty_driver;     tty_driver ->driver_state = uart_driver;
  • 设置tty_driver的操作函数为uart_ops(tty_operations类型)中的操作函数:
  • 调用tty_register_driver():根据tty_driver里的数据来注册字符设备(来自于uart_driver);并添加到tty_drivers链表;调用tty_register_device()产生设备文件。 
 1 int uart_register_driver(struct uart_driver *drv)
 2 {
 3         struct tty_driver *normal = NULL;
 4         int i, retval;        BUG_ON(drv->state);        /*
 5          * Maybe we should be using a slab cache for this, especially if
 6          * we have a large number of ports to handle.
 7          */
 8         drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
 9         retval = -ENOMEM;
10         if (!drv->state)
11                 goto out;        normal  = alloc_tty_driver(drv->nr);
12         if (!normal)
13                 goto out;        drv->tty_driver = normal;        normal->owner           = drv->owner;
14         normal->driver_name     = drv->driver_name;
15         normal->name            = drv->dev_name;
16         normal->major           = drv->major;
17         normal->minor_start     = drv->minor;
18         normal->type            = TTY_DRIVER_TYPE_SERIAL;
19         normal->subtype         = SERIAL_TYPE_NORMAL;
20         normal->init_termios    = tty_std_termios;
21         normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
22         normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
23         normal->flags           = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
24         normal->driver_state    = drv;
25         tty_set_operations(normal, &uart_ops);        /*
26          * Initialise the UART state(s).
27          */
28         for (i = 0; i < drv->nr; i++) {
29                 struct uart_state *state = drv->state + i;                state->close_delay     = 500;   /* .5 seconds */
30                 state->closing_wait    = 30000; /* 30 seconds */                 mutex_init(&state->mutex);
31         }        retval = tty_register_driver(normal);
32  out:
33         if (retval < 0) {
34                 put_tty_driver(normal);
35                 kfree(drv->state);
36         }
37         return retval;
38 }
uart_register_driver

 

1.2 tty_register_driver分析

与传统的字符设备驱动程序完全一致,主要做了一下工作:

  • 创建字符设备
  • 注册字符设备
  • 设置udev,创建/dev节点,名称为"%s%d", driver->name, index + driver->name_base,
                       normal->name = uart_driver->dev_name; //来自于uart_driver= "ttyS", //见struct uart_driver serial8250_reg的定义。
                       driver->name_base =0;
                       driver->num=(0--- driver->num);  // driver->num = uart_driver->nr = UART_NR = 8 
                                   因此创建的节点名为:/dev/ttySx  x=(0…7)
  • Proc文件系统操作; 
 1 int tty_register_driver(struct tty_driver *driver)
 2 {
 3         int error;
 4         int i;
 5         dev_t dev;
 6         void **p = NULL;        if (driver->flags & TTY_DRIVER_INSTALLED)
 7                 return 0;        if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
 8                 p = kzalloc(driver->num * 3 * sizeof(void *), GFP_KERNEL);
 9                 if (!p)
10                         return -ENOMEM;
11         }        if (!driver->major) {
12                 error = alloc_chrdev_region(&dev, driver->minor_start, driver->num,
13                                                 driver->name);
14                 if (!error) {
15                         driver->major = MAJOR(dev);
16                         driver->minor_start = MINOR(dev);
17                 }
18         } else {
19                 dev = MKDEV(driver->major, driver->minor_start);
20                 error = register_chrdev_region(dev, driver->num, driver->name);
21         }
22         if (error < 0) {
23                 kfree(p);
24                 return error;
25         }        if (p) {
26                 driver->ttys = (struct tty_struct **)p;
27                 driver->termios = (struct ktermios **)(p + driver->num);
28                 driver->termios_locked = (struct ktermios **)(p + driver->num * 2);
29         } else {
30                 driver->ttys = NULL;
31                 driver->termios = NULL;
32                 driver->termios_locked = NULL;
33         }        cdev_init(&driver->cdev, &tty_fops);
34         driver->cdev.owner = driver->owner;
35         error = cdev_add(&driver->cdev, dev, driver->num);         if (error) {
36                 unregister_chrdev_region(dev, driver->num);
37                 driver->ttys = NULL;
38                 driver->termios = driver->termios_locked = NULL;
39                 kfree(p);
40                 return error;
41         }        if (!driver->put_char)
42                 driver->put_char = tty_default_put_char;        mutex_lock(&tty_mutex);
43         list_add(&driver->tty_drivers, &tty_drivers);
44         mutex_unlock(&tty_mutex);        if ( !(driver->flags & TTY_DRIVER_DYNAMIC_DEV) ) {
45                 for(i = 0; i < driver->num; i++)
46                     tty_register_device(driver, i, NULL);
47         }
48         proc_tty_register_driver(driver);
49         return 0;
50 }
tty_register_driver

 

此时,内核已经注册了tty_drivers到全局链表tty_drivers。

 

1.3 serial8250_register_ports()函数分析

主要完成以下任务:

  • 为端口号line赋值 
  • 初始化定时器
  • 为uart_8250_port->uart_port.ops赋值= &serial8250_pops
  • 为uart_8250_port[].uart_port->device赋值
  • 将uart_8250_port[].uart_port挂入uart_driver->state[]->port 
1 static void __init serial8250_register_ports(struct uart_driver *drv, struct device *dev)
2 {
3         int i;        serial8250_isa_init_ports();        for (i = 0; i < nr_uarts; i++) {
4                 struct uart_8250_port *up = &serial8250_ports[i];                up->port.dev = dev;
5                 uart_add_one_port(drv, &up->port);
6         }
7 }
serial8250_register_ports

 

 

1.4 serial8250_probe()函数分析

通过struct plat_serial8250_port *p = dev->dev.platform_data获取platform_device的设备私有数据(里面一般包括mapbase、irq、iotype等),将这些数据赋给uart_port,然后调用:

serial8250_register_port()--->uart_add_one_port(&serial8250_reg, &uart->port)

将uart_port注册到uart_driver->state[]->port里面。

 1 static int __devinit serial8250_probe(struct platform_device *dev)
 2 {
 3         struct plat_serial8250_port *p = dev->dev.platform_data;
 4         struct uart_port port;
 5         int ret, i;
 6  
 7         memset(&port, 0, sizeof(struct uart_port));
 8  
 9         for (i = 0; p && p->flags != 0; p++, i++) {
10                 port.iobase     = p->iobase;
11                 port.membase    = p->membase;
12                 port.irq        = p->irq;
13                 port.uartclk    = p->uartclk;
14                 port.regshift   = p->regshift;
15                 port.iotype     = p->iotype;
16                 port.flags      = p->flags;
17                 port.mapbase    = p->mapbase;
18                 port.hub6       = p->hub6;
19                 port.dev        = &dev->dev;
20                 if (share_irqs)
21                         port.flags |= UPF_SHARE_IRQ;
22                 ret = serial8250_register_port(&port);
23                 if (ret < 0) {
24                         dev_err(&dev->dev, "unable to register port at index %d "
25                                 "(IO%lx MEM%llx IRQ%d): %d\\n", i,
26                                 p->iobase, (unsigned long long)p->mapbase,
27                                 p->irq, ret);
28                 }
29         }
30         return 0;
31 }
serial8250_probe

 

然后,我们来看设备的打开过程

以/dev/ttyS0为例。

根据系统在前面在此字符设备注册的fops,在open()后,系统应该是进入tty_fops的tty_open()函数。

可以明确:

tty_struct结构是在tty_open()时构建;

tty_struct保存在file->private_data; 
        以后的操作通过filp就可以找到tty_struct

然后通过tty_struct->tty_driver->open(tty_struct*, filp)调用的是tty_operations uart_ops.open =uart_open(serile_core.c);通过 uart_register_driver()->tty_set_operations(normal, &uart_ops)注册。
        tty_operations里的函数都是以(tty_struct, file* filp) 为参数。

而在uart_open(tty_struct*, filp)里,进行一些初始化后,调用了uart_startup(state, 0),此函数主要做了两件事:
        1)分配并初始化transmit 和 temporary缓冲区circ_buf
        2)调用port->ops->startup(port
                port=state.port |state = uart_driver->state[] |uart_driver=tty_struct->tty_driver->driver_state 

 1 static int tty_open(int input, int output, int primary, void *d,
 2                     char **dev_out)
 3 {
 4         struct tty_chan *data = d;
 5         int fd, err;        fd = os_open_file(data->dev, of_set_rw(OPENFLAGS(), input, output), 0);
 6         if(fd < 0)
 7                 return fd;        if(data->raw){
 8                 CATCH_EINTR(err = tcgetattr(fd, &data->tt));
 9                 if(err)
10                         return err;                err = raw(fd);
11                 if(err)
12                         return err;
13         }        *dev_out = data->dev;
14         return fd;
15 }
tty_open

  

总结

tty_open()后,创建了tty_struct,并保存在filp中;再调用uart层的tty_operations->uart_ops.open(),在里面创建了发送的circ_buf;然后调用了uart_port->uart_ops->open(tty, filp)。

tty_struct对应一个已经打开的具体tty设备。 

 

3 TTY设备的读

TTY设备的读分为两部分:首先是进程读取tty_struct对应的缓冲区并阻塞当前进程;然后设备中断里,接收数据,唤醒进程的读操作。

程序首先进入tty_read():

  • 首先通过file->private_data获取tty_struct,然后再获取tty_ldisc;
  • 最后调用tty_ldisc->read。对于N_TTY即tty_ldisc_N_TTY.read()=read_chan()  
 1 static ssize_t tty_read(struct file * file, char __user * buf, size_t count,
 2                         loff_t *ppos)
 3 {
 4         int i;
 5         struct tty_struct * tty;
 6         struct inode *inode;
 7         struct tty_ldisc *ld;
 8         tty = (struct tty_struct *)file->private_data;
 9         inode = file->f_path.dentry->d_inode;
10         if (tty_paranoia_check(tty, inode, "tty_read"))
11                 return -EIO;
12         if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
13                 return -EIO;
14         /* We want to wait for the line discipline to sort out in this
15            situation */
16         ld = tty_ldisc_ref_wait(tty);
17         lock_kernel();
18         if (ld->read)
19                 i = (ld->read)(tty,file,buf,count);
20         else
21                 i = -EIO;
22         tty_ldisc_deref(ld);
23         unlock_kernel();
24         if (i > 0)
25                 inode->i_atime = current_fs_time(inode->i_sb);
26         return i;
tty_read 

 

3.1 read_chan()

  • 初始化延迟工作队列:init_dev()==>initialize_tty_struct()==>INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc)
  • tty->read_wait只被n_tty_receive_buf()函数(或里面的分支)调用;
  • n_tty_receive_buf()只被flush_to_ldisc()调用
  • 而tty_flip_buffer_push()有两种方式来调用flush_to_ldisc(): 
         1)tty->low_latency===> flush_to_ldisc(&tty->buf.work.work); 
         2)schedule_delayed_work(&tty->buf.work, 1); 
    两者都是调用flush_to_ldisc(),不同点在于后者是延迟执行flush_to_ldisc()。延迟工作队列是在initialize_tty_struct()===>INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);中进行初始化的。

对于驱动层,调用轨迹如下:

在open()操作里申请中断;在中断里唤醒进程。

tty_open()==>………==>serial8250_startup()==>serial_link_irq_chain()==>request_irq()--------申请中断

serial8250_interrupt()--------------------------------------------------------------------------------------------处理中断

    ->serial8250_handle_port()

        ->receive_chars()

    -> uart_insert_char()               //接收字符,存入tty_buffer,tty_struct包含tty_bufhead

           ->tty_insert_flip_char()  //而tty_bufhead包含三个tty_buffer成员:head、tail、free
    ->tty_flip_buffer_push()

    -> flush_to_ldisc()

           -> n_tty_receive_buf()

                -> memcpy(tty->read_buf + tty->read_head, cp, i);  //拷贝数据至tty->read_buf

                     ->tty->read_cnt += i                   //指示接收buff的字符数。

                                                                      //与read_chan()-->input_available_p()
                                                                 里对tty->read_cnt的判断对应

                            ->wake_up(&tty->read_wait)          //唤醒进程

 

大致是下图的流程:

调用tty_insert_flip_char或者tty_insert_flip_string将数据放入tty的缓存tty->tty_buffer;然后调用tty_flip_buffer_push(),将数据从tty缓存拷贝至ldisc缓存。

  • tty_insert_flip_string:将hardware driver中的数据缓冲到tty_buffer中,而这个tty_buffer指针则是在tty_port->buf->tail.
  • tty_flip_buffer_push:将tty_buffer也即tty驱动层缓冲区数据推到tty线路规程层缓冲区,否则tty核心层无法读取到数据,这样用户也就无法从tty核心层取到数据,可以理解为userspace->tty核心->line discipline->tty驱动.
    • 源码中tty_flip_buffer_push会启动flush_to_ldisc的work, 在work进程中会把tty_buffer中的数据推到ldisc的缓冲区。
    • userspace->read->tty_read->n_tty_read(tty_ldisc_ops)读取ldisc缓冲区数据

 

   

  1 static ssize_t read_chan(struct tty_struct *tty, struct file *file,
  2                          unsigned char __user *buf, size_t nr)
  3 {
  4         unsigned char __user *b = buf;
  5         DECLARE_WAITQUEUE(wait, current);
  6         int c;
  7         int minimum, time;
  8         ssize_t retval = 0;
  9         ssize_t size;
 10         long timeout;
 11         unsigned long flags;
 12  
 13 do_it_again:
 14  
 15         if (!tty->read_buf) {
 16                 printk("n_tty_read_chan: called with read_buf == NULL?!?\\n");
 17                 return -EIO;
 18         }
 19  
 20         c = job_control(tty, file);
 21         if(c < 0)
 22                 return c;
 23  
 24         minimum = time = 0;
 25         timeout = MAX_SCHEDULE_TIMEOUT;
 26         if (!tty->icanon) {
 27                 time = (HZ / 10) * TIME_CHAR(tty);
 28                 minimum = MIN_CHAR(tty);
 29                 if (minimum) {
 30                         if (time)
 31                                 tty->minimum_to_wake = 1;
 32                         else if (!waitqueue_active(&tty->read_wait) ||
 33                                  (tty->minimum_to_wake > minimum))
 34                                 tty->minimum_to_wake = minimum;
 35                 } else {
 36                         timeout = 0;
 37                         if (time) {
 38                                 timeout = time;
 39                                 time = 0;
 40                         }
 41                         tty->minimum_to_wake = minimum = 1;
 42                 }
 43         }
 44  
 45         /*
 46          *      Internal serialization of reads.
 47          */
 48         if (file->f_flags & O_NONBLOCK) {
 49                 if (!mutex_trylock(&tty->atomic_read_lock))
 50                         return -EAGAIN;
 51         }
 52         else {
 53                 if (mutex_lock_interruptible(&tty->atomic_read_lock))
 54                         return -ERESTARTSYS;
 55         }
 56  
 57         add_wait_queue(&tty->read_wait, &wait);
 58         while (nr) {
 59                 /* First test for status change. */
 60                 if (tty->packet && tty->link->ctrl_status) {
 61                         unsigned char cs;
 62                         if (b != buf)
 63                                 break;
 64                         cs = tty->link->ctrl_status;
 65                         tty->link->ctrl_status = 0;
 66                         if (tty_put_user(tty, cs, b++)) {
 67                                 retval = -EFAULT;
 68                                 b--;
 69                                 break;
 70                         }
 71                         nr--;
 72                         break;
 73                 }
 74                 /* This statement must be first before checking for input
 75                    so that any interrupt will set the state back to
 76                    TASK_RUNNING. */
 77                 set_current_state(TASK_INTERRUPTIBLE);
 78  
 79                 if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
 80                     ((minimum - (b - buf)) >= 1))
 81                         tty->minimum_to_wake = (minimum - (b - buf));
 82  
 83                 if (!input_available_p(tty, 0)) {
 84                         if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
 85                                 retval = -EIO;
 86                                 break;
 87                         }
 88                         if (tty_hung_up_p(file))
 89                                 break;
 90                         if (!timeout)
 91                                 break;
 92                         if (file->f_flags & O_NONBLOCK) {
 93                                 retval = -EAGAIN;
 94                                 break;
 95                         }
 96                         if (signal_pending(current)) {
 97                                 retval = -ERESTARTSYS;
 98                                 break;
 99                         }
100                         n_tty_set_room(tty);
101                         timeout = schedule_timeout(timeout);
102                         continue;
103 tty初探—uart驱动框架分析

tty初探—uart驱动框架分析uart_add_one_port

tty初探—uart驱动框架分析

[tty与uart]ldisc

驱动大全之UART子系统

Linux中tty框架与uart框架之间的调用关系剖析转