当前位置: 首页 > news >正文

[uart]3.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                 }
104                 __set_current_state(TASK_RUNNING);
105  
106                 /* Deal with packet mode. */
107                 if (tty->packet && b == buf) {
108                         if (tty_put_user(tty, TIOCPKT_DATA, b++)) {
109                                 retval = -EFAULT;
110                                 b--;
111                                 break;
112                         }
113                         nr--;
114                 }
115  
116                 if (tty->icanon) {
117                         /* N.B. avoid overrun if nr == 0 */
118                         while (nr && tty->read_cnt) {
119                                 int eol;
120  
121                                 eol = test_and_clear_bit(tty->read_tail,
122                                                 tty->read_flags);
123                                 c = tty->read_buf[tty->read_tail];
124                                 spin_lock_irqsave(&tty->read_lock, flags);
125                                 tty->read_tail = ((tty->read_tail+1) &
126                                                   (N_TTY_BUF_SIZE-1));
127                                 tty->read_cnt--;
128                                 if (eol) {
129                                         /* this test should be redundant:
130                                          * we shouldn't be reading data if
131                                          * canon_data is 0
132                                          */
133                                         if (--tty->canon_data < 0)
134                                                 tty->canon_data = 0;
135                                 }
136                                 spin_unlock_irqrestore(&tty->read_lock, flags);
137  
138                                 if (!eol || (c != __DISABLED_CHAR)) {
139                                         if (tty_put_user(tty, c, b++)) {
140                                                 retval = -EFAULT;
141                                                 b--;
142                                                 break;
143                                         }
144                                         nr--;
145                                 }
146                                 if (eol) {
147                                         tty_audit_push(tty);
148                                         break;
149                                 }
150                         }
151                         if (retval)
152                                 break;
153                 } else {
154                         int uncopied;
155                         uncopied = copy_from_read_buf(tty, &b, &nr);
156                         uncopied += copy_from_read_buf(tty, &b, &nr);
157                         if (uncopied) {
158                                 retval = -EFAULT;
159                                 break;
160                         }
161                 }
162  
163                 /* If there is enough space in the read buffer now, let the
164                  * low-level driver know. We use n_tty_chars_in_buffer() to
165                  * check the buffer, as it now knows about canonical mode.
166                  * Otherwise, if the driver is throttled and the line is
167                  * longer than TTY_THRESHOLD_UNTHROTTLE in canonical mode,
168                  * we won't get any more characters.
169                  */
170                 if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) {
171                         n_tty_set_room(tty);
172                         check_unthrottle(tty);
173                 }
174  
175                 if (b - buf >= minimum)
176                         break;
177                 if (time)
178                         timeout = time;
179         }
180         mutex_unlock(&tty->atomic_read_lock);
181         remove_wait_queue(&tty->read_wait, &wait);
182  
183         if (!waitqueue_active(&tty->read_wait))
184                 tty->minimum_to_wake = minimum;
185  
186         __set_current_state(TASK_RUNNING);
187         size = b - buf;
188         if (size) {
189                 retval = size;
190                 if (nr)
191                         clear_bit(TTY_PUSH, &tty->flags);
192         } else if (test_and_clear_bit(TTY_PUSH, &tty->flags))
193                  goto do_it_again;
194  
195         n_tty_set_room(tty);
196  
197         return retval;
198 }
read_chan

 

 

 4 TTY设备的写

首先调用tty_write. 

 1 static ssize_t tty_write(struct file * file, const char __user * buf, size_t count,
 2                          loff_t *ppos)
 3 {
 4         struct tty_struct * tty;
 5         struct inode *inode = file->f_path.dentry->d_inode;
 6         ssize_t ret;
 7         struct tty_ldisc *ld;
 8  
 9         tty = (struct tty_struct *)file->private_data;
10         if (tty_paranoia_check(tty, inode, "tty_write"))
11                 return -EIO;
12         if (!tty || !tty->driver->write || (test_bit(TTY_IO_ERROR, &tty->flags)))
13                 return -EIO;
14  
15         ld = tty_ldisc_ref_wait(tty);
16         if (!ld->write)
17                 ret = -EIO;
18         else
19                 ret = do_tty_write(ld->write, tty, file, buf, count);
20         tty_ldisc_deref(ld);
21         return ret;
22 }
tty_write

  

TTY设备写涉及写进程、中断ISR、即tasklet_action三部分的配合: 

 

 

应用APP函数调用

tty_write()---------------------------------------------------------------------------------------------------tty_io.c

     do_tty_write()

           copy_from_user()             //将用户空间数据copy至tty->write_buf

           write ()                            //对于N_TTY,即tty_ldisc_N_TTY.write()=write_chan()

                add_wait_queue()-------------------------------------------------------------------------n_tty.c

                                                                 //添加等待队列

                tty->driver->flush_chars(tty);      //struct tty_operations uart_ops. flush_chars=uart_flush_chars()

                      uart_start()---------------------------------------------------------------------------serial_core.c

                             __uart_start()

                                     uart_port->ops->start_tx()   //uart_ops serial8250_pops.start_tx = serial8250_start_tx

                                           transmit_chars()               //启动真正发送------------------------------8250.c

                                                   serial_out(up, UART_TX, xmit->buf[xmit->tail])      //copy至芯片发送buffer

                                           uart_write_wakeup(struct uart_port *port)           //调度tasklet_schedule()

                                                   tasklet_schedule(&info->tlet);

                        schedule();                 //进程睡眠

中断函数调用:

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

         serial8250_handle_port()------------------------------------------------------------------------8250.c

                   if(Transmit-hold-register empty)

                            transmit_chars()

                                     serial_out(up, UART_TX, xmit->buf[xmit->tail])      //copy至芯片发送缓冲

                                     uart_write_wakeup()---------------------------------------------------serial_core.c

                                               tasklet_schedule()                                    //调度tasklet_schedule()

tasklet在tty_open()-->……-->uart_open()-->uart_get()
        -->tasklet_init(&state->info->tlet, uart_tasklet_action,state)中进行初始化。
       【tty_open()-->tty_struct.tty_driver.open()=uart_open()-->uart_get()-->tasklet_init()】

tasklet_action()调用:

经过tasklet_schedule ()后执行uart_tasklet_action。

uart_tasklet_action ()

         tty_wakeup()

                   ld->write_wakeup(tty)//即n_tty_write_wakeup():发送信号SIGIO给fasync_struct所描述的PID

                   wake_up_interruptible(&tty->write_wait);    //唤醒写进程

 

总结

在理清了数据走向和函数调用关系后,我们可以清晰的知道开发TTY驱动,需要我们做什么:

  • 定义uart_driver数据结构;
  • 定义uart_port数据结构;
  • 完成uart_ops操作函数集合。

最后放一张从进程、vfs、tty_core、serial_core到uart驱动各个数据结构之间的相互关系图: 

博客推荐

tty驱动分析:http://blog.csdn.net/lizuobin2/article/details/51773305 ,对数据流和读写说的很好

问题

1. 怎样理解tty核心层、tty驱动层、线路规程层、串口核心层、串口驱动层?

核心层其实就是operation的实现方法部分,所以

  • tty核心层为tty_read/tty_write等,包括n_tty.c(line discipline)和tty_io.c中
  • serial核心层为uart_read/uart_write等,包括serial_core.c和imx.c(freescale chip).

 2. TTY这层存在的作用是什么?

TTY用来抽象串行接口的设备,抽象了串行接口设备所需要的特性、功能,抽象后,一个tty设备即可表示一个串行输入、输出接口(比如控制台、keypad、串口、pty设备)等

转载于:https://www.cnblogs.com/aaronLinux/p/5616153.html

相关文章:

  • 重构心法——分解大函数
  • 你知道hover、active这四个伪类为什么要按顺序写吗
  • 用CHttpFile实现简单的GET/POST数据【转】
  • 《入门经典》——6.27
  • Distributed systems theory for the distributed systems engineer
  • python 学习 异常处理
  • c# 定时器
  • 图论(二分图最大权独立点集):COGS 2051. 王者之剑
  • flow.ci + Github + Slack 一步步搭建 Python 自动化持续集成
  • pct xcode7
  • 高并发性能调试经验分享
  • java中String类、StringBuilder类和StringBuffer类详解
  • 如何确认软件测试结束的呢?
  • SpringMVC(一)
  • 【转】web移动端一些常用知识
  • 2017-08-04 前端日报
  • canvas 高仿 Apple Watch 表盘
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • js学习笔记
  • ng6--错误信息小结(持续更新)
  • React+TypeScript入门
  • 番外篇1:在Windows环境下安装JDK
  • 翻译 | 老司机带你秒懂内存管理 - 第一部(共三部)
  • 山寨一个 Promise
  • 小程序滚动组件,左边导航栏与右边内容联动效果实现
  • PostgreSQL之连接数修改
  • raise 与 raise ... from 的区别
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • ​卜东波研究员:高观点下的少儿计算思维
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • #1015 : KMP算法
  • #QT(智能家居界面-界面切换)
  • (2021|NIPS,扩散,无条件分数估计,条件分数估计)无分类器引导扩散
  • (4)Elastix图像配准:3D图像
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (附源码)ssm跨平台教学系统 毕业设计 280843
  • (机器学习-深度学习快速入门)第三章机器学习-第二节:机器学习模型之线性回归
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (十七)Flask之大型项目目录结构示例【二扣蓝图】
  • (五)网络优化与超参数选择--九五小庞
  • (原创)Stanford Machine Learning (by Andrew NG) --- (week 9) Anomaly DetectionRecommender Systems...
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .NET Core 将实体类转换为 SQL(ORM 映射)
  • .net core 控制台应用程序读取配置文件app.config
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .NET Standard、.NET Framework 、.NET Core三者的关系与区别?
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .net获取当前url各种属性(文件名、参数、域名 等)的方法
  • .NET连接数据库方式
  • .Net语言中的StringBuilder:入门到精通
  • .pub是什么文件_Rust 模块和文件 - 「译」
  • /bin/bash^M: bad interpreter: No such file or directory
  • /etc/X11/xorg.conf 文件被误改后进不了图形化界面