• 一、uart&tty驱动


      1 一.I.MX6 UART驱动
      2 文件路径:linux_IMX6_CoreC_3.0.35_for_Linuxdrivers	tyserialimx.c
      3 1.驱动入口函数:imx_serial_init()
      4 1.1    static int __init imx_serial_init(void)
      5           ret = uart_register_driver(&imx_reg);                //驱动加载的时候调用了这个函数注册串口驱动,将参数imx_reg注册进了tty层
      6             struct tty_driver *normal;
      7             normal = alloc_tty_driver(drv->nr);                //申请tty驱动,串口设备套上了一层tty驱动的外壳
      8             ...
      9             tty_set_operations(normal, &uart_ops);
     10             //上面这段代码可以得出一个结论,uart_driver的数据类型其实就是tty_driver,两者进行数据转换之后注册进了tty层。
     11             //tty_set_operations(normal, &uart_ops);将uart的操作函数和tty关联起来,应用层对于tty的操作都将对应到uart的操作
     12 
     13             retval = tty_register_driver(normal);            //向TTY核心层注册一个TTY驱动,所以串口设备其实就是一个tty类型的设备
     14                 alloc_chrdev_region(&dev, driver->minor_start, driver->num, driver->name);
     15                 register_chrdev_region(dev, driver->num, driver->name);
     16                 cdev_init(&driver->cdev, &tty_fops);        //初始化设备,注意这里将指针调用关系赋给了cdev
     17 
     18         ret = platform_driver_register(&serial_imx_driver);    //注册平台驱动
     19             //serial_imx_driver变量类型如下
     20             static struct platform_driver serial_imx_driver = {
     21             .probe        = serial_imx_probe,                    //当匹配到设备之后,这句函数得到调用,转去分析这个函数
     22             .remove        = serial_imx_remove,
     23 
     24             .suspend    = serial_imx_suspend,
     25             .resume        = serial_imx_resume,
     26             .driver        = {
     27             .name    = "imx-uart",
     28             .owner    = THIS_MODULE,
     29             },
     30         };
     31 1.2    serial_imx_probe
     32         static int serial_imx_probe(struct platform_device *pdev)
     33             sport->port.dev = &pdev->dev;
     34             //imx_pops是串口接收数据、发送数据的相关函数(注意这里要区别前面的串口操作函数)
     35             //我觉得前面的串口操作函数是从应用层或者tty的角度来看,使用者也仅仅是应用层(tty层)
     36             //这里的imx_pops里面众多的操作函数是从底层的角度去看,也就是底层和硬件相关的中断接收、发送相关。
     37             sport->port.ops = &imx_pops;
     38             sport->port.flags = UPF_BOOT_AUTOCONF;
     39             sport->port.line = pdev->id;
     40             init_timer(&sport->timer);
     41             sport->timer.function = imx_timeout;
     42             sport->timer.data     = (unsigned long)sport;    
     43             ...
     44             ...
     45             ...
     46             ret = uart_add_one_port(&imx_reg, &sport->port);    //关键:为uart_driver增加一个端口
     47             //列出imx_pops重要的几个函数
     48             static struct uart_ops imx_pops = {
     49                 ...
     50                 .stop_tx    = imx_stop_tx,
     51                 .start_tx    = imx_start_tx,                        //串口发送
     52                 .stop_rx    = imx_stop_rx,
     53                 .startup    = imx_startup,                        //中断发送相关的函数,接下来我们分析这个发送函数
     54                 ...
     55             };
     56 
     57 1.3 static int imx_startup(struct uart_port *port)                //做一些串口的初始化操作
     58     {
     59         struct imx_port *sport = (struct imx_port *)port;
     60         struct tty_struct *tty;
     61         ...
     62         clk_enable(sport->clk);                                    //使能时钟
     63 
     64         /* disable the DREN bit (Data Ready interrupt enable) before
     65          * requesting IRQs
     66         */
     67         temp = readl(sport->port.membase + UCR4);
     68         ...
     69         writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
     70         ...
     71         /*
     72          * Allocate the IRQ(s) i.MX1 has three interrupts whereas later
     73         * chips only have one interrupt.
     74         */
     75         retval = request_irq(sport->rxirq, imx_rxint, 0, DRIVER_NAME, sport);//关键:在这里申请了接收中断函数(同时注册了中断接收函数imx_rxint)
     76         ...
     77         retval = request_irq(sport->txirq, imx_txint, 0, DRIVER_NAME, sport);//申请注册发送中断函数(这次分析我们不关心发送中断)
     78         /* Enable the SDMA for uart. */
     79         if (sport->enable_dma)                //如果配置串口使用DMA
     80         {
     81             int ret;
     82             ret = imx_uart_dma_init(sport);    //DMA的初始化
     83                 //下面几句代码都是DMA初始化相关,平台通用,不展开说明啦
     84                 sport->dma_data.priority = DMA_PRIO_HIGH;
     85                 sport->dma_data.dma_request = pdata->dma_req_rx;
     86                 sport->dma_data.peripheral_type = IMX_DMATYPE_UART;
     87                 sport->dma_chan_rx = dma_request_channel(mask, imx_uart_filter, sport);
     88                 ...
     89                 slave_config.direction = DMA_DEV_TO_MEM;
     90                 slave_config.src_addr = sport->port.mapbase + URXD0;
     91             //注意:如果配置串口使用DMA,这里初始化了一个工作队列(中断下半部的一种),来进行数据的接收和发送
     92             //听说3.0.35版本的内核DMA驱动有问题,不知道是不是真的,待证实,实际使用的时候,确实是发现DMA在连续传输大量数据的时候会出现数据丢失的情况
     93             INIT_WORK(&sport->tsk_dma_tx, dma_tx_work);
     94             INIT_WORK(&sport->tsk_dma_rx, dma_rx_work);                            //基于DMA的串口中断接收函数,也是在这里注册了一个工作队列
     95                 dma_rx_work(struct work_struct *w)
     96                     tty_insert_flip_string(tty, sport->rx_buf, sport->rx_bytes);//关键的函数调用:将数据放到tty数据缓冲区
     97                         tty_insert_flip_string_fixed_flag(tty, chars, TTY_NORMAL, size);
     98                             struct tty_buffer *tb = tty->buf.tail;
     99                                 memcpy(tb->char_buf_ptr + tb->used, chars, space);
    100                                 memset(tb->flag_buf_ptr + tb->used, flag, space);
    101                     //如果不调用下面的函数,只是将数据放到tty缓冲区,tty也是获取不到数据的
    102                     tty_flip_buffer_push(tty);            //将缓冲区的数据推到tty当中,其实内部是一个工作队列的调度(在tty初始化的时候注册了这个工作队列),等于是通知tty的线路规程获取数据的意思
    103                         schedule_work(&tty->buf.work);    //工作调度,实际上调用了flush_to_ldisc函数(这个函数好像不能在中断中调用
    104                                                         //,但是我发现也有其他驱动直接在中断调用了,实际上我在中断中调用业一直没发现有什么问题)
    105             init_waitqueue_head(&sport->dma_wait);
    106         }
    107         ...
    108         spin_lock_irqsave(&sport->port.lock, flags);
    109         /*
    110         * Finally, clear and enable interrupts
    111         */
    112         writel(USR1_RTSD, sport->port.membase + USR1);    //使能中断
    113 
    114     }
    115     
    116 1.4    //在前面刚进入函数的时候,我们申请了串口中断,同时注册了imx_rxint接收函数,接下来分析这个函数
    117     retval = request_irq(sport->rxirq, imx_rxint, 0, DRIVER_NAME, sport);
    118             spin_lock_irqsave(&sport->port.lock,flags);                //先获取自旋锁
    119             
    120             while (readl(sport->port.membase + USR2) & USR2_RDR)    //判断串口寄存器状态,开始接收字节数据
    121             {
    122                 flg = TTY_NORMAL;
    123                 sport->port.icount.rx++;
    124                 rx = readl(sport->port.membase + URXD0);
    125                 temp = readl(sport->port.membase + USR2);
    126                 ...
    127                 if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
    128                     continue;
    129                 if (rx & URXD_BRK)
    130                     flg = TTY_BREAK;
    131                 else if (rx & URXD_PRERR)
    132                     flg = TTY_PARITY;
    133                 else if (rx & URXD_FRMERR)
    134                     flg = TTY_FRAME;
    135                 if (rx & URXD_OVRRUN)
    136                     flg = TTY_OVERRUN;
    137             }
    138             tty_insert_flip_char(tty, rx, flg);
    139                 tty_insert_flip_string_flags(tty, &ch, &flag, 1);
    140                     struct tty_buffer *tb = tty->buf.tail;
    141                     memcpy(tb->char_buf_ptr + tb->used, chars, space);
    142                     memcpy(tb->flag_buf_ptr + tb->used, flags, space);
    143 out:
    144     spin_unlock_irqrestore(&sport->port.lock,flags);//释放自旋锁
    145     tty_flip_buffer_push(tty);//里面有工作队列的调度,将数据“刷新”到tty
    146     return IRQ_HANDLED;
    147 
    148 2.uart操作函数结构体
    149 static const struct tty_operations uart_ops = {
    150     .open        = uart_open,
    151     .close        = uart_close,
    152     .write        = uart_write,
    153     .put_char    = uart_put_char,
    154     ...
    155     ...
    156     ...
    157 };
    158 //从结构体的类型我们知道,uart_ops的数据类型就是tty_operations 类型,对应tty的文件操作函数
    159 //所以tty的操作路径就对应着tty_ops-->uart_driver-->uart->ops
    160 
    161 3.uart_open函数
    162     static int uart_open(struct tty_struct *tty, struct file *filp)
    163         struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
    164 
    165 4.uart_close函数
    166     static void uart_close(struct tty_struct *tty, struct file *filp)
    167         struct uart_state *state = tty->driver_data;
    168 
    169 5.uart_write函数
    170     static int uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
    171         struct uart_state *state = tty->driver_data;
    172 //在前面,normal->driver_state    = drv;将串口的私有数据赋值给tty
    173 //上面3、4、5这几个函数是在我们打开串口或者做串口操作的时候,将数据从tty拿出来(因为打开串口的时候,
    174 //就是tty去操作设备号对应将设备的信息传递到驱动中来)
    175 //这几个函数都是应用层在操作串口的时候才会调用,与串口数据发送没有关系,所以就不展开分析了。
    176 
    177 
    178 二、tty驱动分析(不针对特定处理器平台)
    179 文件路径:linux_IMX6_CoreC_3.0.35_for_Linuxdrivers	ty	ty_io.c
    180          linux_IMX6_CoreC_3.0.35_for_Linuxdrivers	ty	ty_buffer.c
    181          linux_IMX6_CoreC_3.0.35_for_Linuxdrivers	ty	ty_ldisc.c
    182 tty由上往下分为tty核心层、tty线路规程、tty驱动,用户空间对应tty设备的操作函数定义及实现都在tty_io.c中。
    183 tty核心层不能直接从tty驱动获取数据,底层的数据经过tty驱动,在经过tty线路规程,再到tty核心层。
    184 1. 线路规程的初始化
    185 //线路规程的初始化是在内核刚启动的时候完成的,文件路径是initmain.c
    186 asmlinkage void __init start_kernel(void)
    187     console_init();
    188         /* Setup the default TTY line discipline. */
    189         tty_ldisc_begin();
    190             /* Setup the default TTY line discipline. */
    191             (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
    192             //分析tty_ldisc_N_TTY结构体变量(属于tty_ldisc_ops类型,接着分析tty_ldisc_ops)
    193 1.1 tty_ldisc_ops(线路规程操作函数结构体)
    194     struct tty_ldisc_ops tty_ldisc_N_TTY = 
    195     {
    196         .magic           = TTY_LDISC_MAGIC,
    197         .name            = "n_tty",
    198         .open            = n_tty_open,
    199         .close           = n_tty_close,
    200         .flush_buffer    = n_tty_flush_buffer,
    201         .chars_in_buffer = n_tty_chars_in_buffer,
    202         .read            = n_tty_read,
    203         .write           = n_tty_write,
    204         .ioctl           = n_tty_ioctl,
    205         .set_termios     = n_tty_set_termios,
    206         .poll            = n_tty_poll,
    207         .receive_buf     = n_tty_receive_buf,
    208         .write_wakeup    = n_tty_write_wakeup
    209     };
    210 //以上这些函数就是tty线路规程在操作数据时候用到的函数,从tty应用层操作向下看就是tty_ops--->tty_ldisc_ops
    211 //前面分析uart驱动时候uart_ops和实际的数据接收和发送函数是分开的,在这里也是类似。
    212 //操作设备是一组函数,实际做数据收发的又是另外一组函数。
    213 1.1.1 n_tty_open函数
    214     static int n_tty_open(struct tty_struct *tty)
    215         tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);//分配tty缓冲区给tty_read_buf,其中N_TTY_BUF_SIZE为4096
    216         ...
    217         reset_buffer_flags(tty);//初始化头尾指针、读计数等
    218             tty->read_head = tty->read_tail = tty->read_cnt = 0;
    219 1.1.2 n_tty_write函数
    220     static ssize_t n_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *buf, size_t nr)
    221         ...
    222         //这里调用到了tty_driver操作函数,因为在之前的tty_open函数中有了tty->ops=driver->ops这样的操作。
    223         //注册的时候初始化了ops
    224         tty->ops->flush_chars(tty);
    225         ...
    226         c = tty->ops->write(tty, b, nr);
    227         
    228 1.1.3 n_tty_read函数
    229     static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, unsigned char __user *buf, size_t nr)
    230         ...
    231         ...
    232         while (nr)
    233         {
    234             c = tty->read_buf[tty->read_tail];//从tty读缓冲区获取数据
    235             ...
    236             tty_put_user(tty, c, b++);
    237                 tty_audit_add_data(tty, &x, 1);
    238                 put_user(x, ptr);
    239             uncopied = copy_from_read_buf(tty, &b, &nr);
    240             uncopied += copy_from_read_buf(tty, &b, &nr);
    241         }
    242 //注意这里tty线路规程对数据的读操作并没有调用到tty驱动层的读函数。
    243 //和tty线路规程对数据的写操作要做个对比和区别。
    244 //tty的数据读取是“被动”读取的,简单讲就是通过串口中断接收字节,然后调度tty初始化时候注册的工作队列来实现数据的读取,
    245 //对应的函数是:
    246     flush_to_ldisc();
    247         ...
    248         disc->ops->receive_buf(tty, char_buf,flag_buf, count);//具体调用的函数是n_tty_receive_buf函数
    249         ...
    250 2. int __init tty_init(void)
    251     //初始化tty设备,tty设备本质上也是一个字符设备
    252     //用户空间对于tty设备的操作都对应到tty_fops结构体里面的函数
    253     cdev_init(&tty_cdev, &tty_fops);//我们重点分析tty操作函数结构体里面的函数集合
    254     cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1);
    255     register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty");
    256     device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");//创建设备
    257     //初始化tty设终端(关于tty的终端,我们暂时不分析)
    258     cdev_init(&console_cdev, &console_fops);
    259     register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console");
    260     consdev = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL, "console");
    261     ...
    262 2.1 tty_buffer_init()函数
    263         INIT_WORK(&tty_buf.work, flush_to_ldisc);//flush_to_ldisc函数绑定到工作队列,当tty_filp_buffer_push函数中调用schedule_work(&tty->buf.work);时候,函数得到调用
    264 2.2 tty_fops结构体
    265     static const struct file_operations tty_fops = {
    266         .llseek        = no_llseek,
    267         .read        = tty_read,                            //注意这里的read函数不是对应uart操作函数里面的read函数(需要做关联),后面会讲到
    268         .write        = tty_write,                        //同上
    269         .poll        = tty_poll,
    270         .unlocked_ioctl    = tty_ioctl,
    271         .compat_ioctl    = tty_compat_ioctl,
    272         .open        = tty_open,
    273         .release    = tty_release,
    274         .fasync        = tty_fasync,
    275     };
    276 
    277 2.3 tty_open函数(打开tty设备时候做的一些初始化操作,建立tty核心和线路规程以及硬件驱动的联系)
    278     static int tty_open(struct inode *inode, struct file *filp)
    279         struct tty_driver *driver;
    280         int index;
    281         dev_t device = inode->i_rdev;
    282         ...
    283         tty = get_current_tty();
    284         tty_free_file(filp);
    285         ...
    286         driver = tty_driver_kref_get(tty->driver);
    287         ...
    288         driver = get_tty_driver(device, &index);        //根据设备号,来查找到tty_driver
    289         tty = tty_init_dev(driver, index, 0);            //初始化tty结构体
    290             initialize_tty_struct(tty, driver, idx);    //关键的一个初始化,在里面建立tty核心层和线路规程的联系
    291                 tty_ldisc_init(tty);                    //初始化线路规程
    292                 tty->buf.head = tty->buf.tail = NULL;    //缓冲区头尾指空
    293                 tty_buffer_init(tty);                    //初始化tty缓冲区
    294                     spin_lock_init(&tty->buf.lock);
    295                     tty->buf.head= NULL;
    296                     tty->buf.tail= NULL;
    297                     tty->buf.free= NULL;
    298                     tty->buf.memory_used= 0;
    299                     //初始化了一个工作队列
    300                     //当在驱动层里receive_chars的最后调用了tty_flip_buffer_push这个函数的时候这个队里得到调度
    301                     INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);//在中断下半部中进行调度时候,flush_to_ldisc会得到调用,接下来分析这个函数
    302                 ...
    303                 tty->ops = driver->ops;                    //tty操作指向tty设备操作(注意这里是指向具体的硬件设备驱动操作)
    304                 tty->dev = tty_get_device(tty);
    305                     dev_t devt = tty_devnum(tty);        //获得设备号
    306                     ...
    307             retval = tty_driver_install_tty(driver, tty);
    308             ...
    309             retval = tty_ldisc_setup(tty, tty->link);    //设置线路规程
    310                 retval = tty_ldisc_open(tty, ld);        //打开线路规程
    311                 tty_ldisc_enable(tty);                    //使能线路规程
    312         ...
    313         tty_add_file(tty, filp);
    314         check_tty_count(tty, "tty_open");                //检查tty被打开多少次
    315         ...
    316         retval = tty->ops->open(tty, filp);                //实际上就是driver->ops->open(注意这里的driver指的是具体硬件的driver)
    317 2.3.1 tty_flip_buffer_push函数
    318     void tty_flip_buffer_push(struct tty_struct *tty)
    319         ...
    320         ...
    321         ...
    322         if (tty->low_latency)
    323             flush_to_ldisc(&tty->buf.work.work);
    324         else
    325             schedule_work(&tty->buf.work);//工作队列调度,内核线程???会调用flush_to_ldisc
    326 
    327 2.3.2 flush_to_ldisc函数
    328 //从tty_buffer中找到数据缓冲区char_buf_ptr,并将这个缓冲区指针传递给线路规程的操作函数receive_buf。
    329 //拷贝数据进tty的read_buf
    330     flush_to_ldisc(struct work_struct *work)
    331         disc->ops->receive_buf(tty, char_buf, flag_buf,count);
    332             n_tty_receive_buf(struct tty_struct *tty, const unsigned char*cp, char *fp, int count);
    333                 n_tty_receive_char(tty,*p);
    334                 ...
    335                 if(tty->ops->flush_chars)
    336                     tty->ops->flush_chars(tty);
    337 
    338 2.4 tty_write函数(应用层调用)
    339     static ssize_t tty_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
    340         struct tty_struct *tty = file_tty(file);
    341          struct tty_ldisc *ld;                                        //tty线路规程结构体,这个结构体很重要
    342         ...
    343         ld = tty_ldisc_ref_wait(tty);                                //等待tty线路规程(所有从tty驱动上来的数据或者从应用层到tty驱动的数据,都要经过线路规程)
    344         ret = do_tty_write(ld->ops->write, tty, file, buf, count);    //do_tty_write这里实际上是调用了ld->ops->write(也就是线路规程的write函数,将数据写入tty缓冲区)
    345                 //等价于调用n_tty_write函数
    346                 //在n_tty_write函数中调用下面tty driver中的操作函数
    347                 tty->ops->flush_chars(tty);
    348                 ...
    349                 c = tty->ops->write(tty, b, nr);
    350             copy_from_user(tty->write_buf, buf, size);                //从用户空间拷贝数据到tty写缓冲中
    351 
    352 2.5 tty_read函数(应用层调用)
    353     static ssize_t tty_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
    354         ...
    355         struct tty_struct *tty = file_tty(file);
    356         ...
    357         i = (ld->ops->read)(tty, file, buf, count);//调用线路规程里面的read函数(n_tty_read)
    358             //下面这几句代码是n_tty_read函数的
    359             while (nr)
    360             {
    361                 c = tty->read_buf[tty->read_tail];//从tty读缓冲区获取数据,实际上是在tty->read_buf的末尾tty->read_tail中读取数据
    362                 ...
    363                 tty_put_user(tty, c, b++);
    364                     tty_audit_add_data(tty, &x, 1);
    365                     put_user(x, ptr);
    366                 uncopied = copy_from_read_buf(tty, &b, &nr);
    367                 uncopied += copy_from_read_buf(tty, &b, &nr);
    368             }
  • 相关阅读:
    64位整数乘法
    HTML中常见问题汇总贴
    题解 牛客【「水」悠悠碧波】
    题解 CF1391B 【Fix You】
    四级-句子
    快速幂||取余运算
    最大子列和
    JvavScript中的函数与对象
    JavaScript中的流程控制语句
    冒泡排序,选择排序,插入排序,归并排序
  • 原文地址:https://www.cnblogs.com/timemachine213/p/12728233.html
Copyright © 2020-2023  润新知