• [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
    ", 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?!?
    ");
     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设备)等

  • 相关阅读:
    300+值得收藏的设计师免费资源站
    Apache 隐藏入口文件 index.php
    Nginx 虚拟主机下支持Pathinfo并隐藏入口文件的完整配置
    Java多线程
    Java注解
    Java异常机制
    面向对象
    数组
    Java方法(函数)
    Java流程控制(Scanner)
  • 原文地址:https://www.cnblogs.com/aaronLinux/p/5616153.html
Copyright © 2020-2023  润新知