Introduction to "serial device driver"     (My study note)

膜拜大神的作品.

Standing on the shoulder of the gaints.

——题记

用什么眼光去看待串口设备

当谈到软件对于串口的抽象实现的时候,人们第一反应可能是设备/dev/ttyS0,这个众所周知的串行设备通信的接口文件(至少在PC端是这种). 因为/dev/ttyS0 是一个char类型的文件,于是一个串口设备通常被觉得是一个char driver 驱动着. 非常傻非常天真。不是这样滴~

其实“char driver”的抽象并不能全然正确的对设备进行抽象描写叙述,

because there is not specific major number associated to each of them. (这句话不是非常懂~)

你能够写新的driver。不要尝试为串口设备加入新的major number (主设备号)

当我们查看串口设备ttyS*   ( ' * '  通配符) ,我们会发现。这厮的major number都是4 有木有!

问题来了----什么让串口设备驱动和普通的字符设备驱动不同捏?

Since a serial communication channel can be used to plug an alphanumeric terminal, a serial device driver must be integrated in the terminal emulator layer, called the ``tty'' abstraction, from the name of ancient
tele-type devices (still in wide use when Unix was being written)


俯瞰 tty 设备管理

灵活有力的 tty handling 由几个模块组成。毕竟tty设备太多了,并且都非常重要。

上图给出了tty core 和 tty driver之间的关系

大多数文件都是放置在 drivers/char
以下。假设不是。这里的文件夹就放在相关的根文件夹下咯

下图给出了和串行设备管理的相关文件拓扑图

 



文件 fs/devices.c 导出接口用于大多数系统资源的注冊,每一个device driver 由 major number 唯一确定。

这就是 为何当我们须要支持新的tty driver的时候,  tty_register_driver() 函数须要获得major number number. 这个函数在tty_io.c 里面定义。而且该文件还定义了和tty 设备相关的文件操作.

设备相关driver 来实现对于设备操作的支持。这些操作就涉及数据的输入输出。数据流的控制以及和更上一层的交互. 这些文件操作和中断一起。实际操控着数据的IO.

数据在用户空间和串口设备驱动之间隔着一个tty 层(tty line discipline),用于实现数据的转换. 这点在LDD3里就有讲~

并非全部的tty 管理操作都在 tty_io.c 里面定义,大多数策略(policy) 是在   line discipline定义的。这是一个软件模块,控制物理的tty I/O 怎样使用.

默认的  line discipline for 叫 N_TTY.  假设 n_tty 处于active 状态,输入数据通过通常的/dev/ 接口和标准的terminal I/O 函数来达到 用户空间.

图中的红线显示出对数据流动的逻辑控制,从硬件通道上至用户空间能够涉及的设备相关文件(/dev/文件夹下的文件)

而链接这一切的是 stryct tty ,在这个结构体里面包括了一个指针,这个指针指向全部相关的模块:

      • file_operations(用于和用户空间通信交互)
      • struct tty_driver则实际掌控着控制硬件操作的部分,
      •  struct tty_ldisc 则列出全部当前 line discipline 的入口指针

为啥这么麻烦?

这类组织管理方式看似麻烦复杂,然而这样的额外付出的复杂性能够获得更加好的灵活性。使得模块更加强大。

便于对于不同的tty设备更改 line discipline.

普通的设备驱动就是在硬件和用户空间之间的通信桥梁。和普通的设备驱动不同, a serial driver 和用户空间没啥关系。a serial driver  接受到来自硬件的设备都不是直接传给用户空间的。它传给  line discipline, 而且 也差点儿不直接接受用户空间数据,它的数据来源于 一个 line discipline method.

一个单独的serial driver不是用户空间和硬件之间的数据转换者.

The task is left to the line discipline, together with all the hairy termios handling. This makes it possible for serial data to be steered to a different user-space access facility than
its associated ttyS device special file.

一些相关的数据结构

这里有三种和tty 管理相关的基本的数据结构

  • struct tty_struct 
  • struct tty_driver: this is the low level hardware handling. At open time, the function get_tty_driver retrieves the driver for the current tty an places it into the driver field of tty_struct,
    where it is further accessed.
  • struct tty_driver {
    
    	    /* the driver states which range of devices it supports */
    short major; /* major device number */
    short minor_start; /* start of minor device number*/
    short num; /* number of devices */ /* and has its own operations */
    int (*open)();
    void (*close)();
    int (*write)();
    int (*ioctl)(); /* device-specific control */ /* return information on buffer state */
    int (*write_room)(); /* how much can be written */
    int (*chars_in_buffer)(); /* how much is there to read */ /* flow control, input and output */
    void (*throttle)();
    void (*unthrottle)();
    void (*stop)();
    void (*start)(); /* and callbacks for /proc/tty/driver/ */
    int (*read_proc)();
    int (*write_proc)();
    };
  • struct tty_ldisc: the structure is referenced by the ldisc field of tty_struct. At open time the field is initialized to reference n_tty, and user programs can change the current
    line discipline via ioctl, as explained in a while.
  • 	struct tty_ldisc {
    
    	    /* routines called from above */
    int (*open)();
    void (*close)();
    ssize_t (*read)();
    ssize_t (*write)();
    int (*ioctl)(); /* routines called from below */
    void (*receive_buf)();
    int (*receive_room)();
    void (*write_wakeup)();
    };

这些数据结构在三个不同的文件中面定义。  tty_struct 这个复杂的大家伙在 include/linux/tty.h 里面,相比另外两个结构体,事实上这个东东用的少~.

include/linux/tty_driver.h and include/linux/tty_ldisc.h 定义了另外两个结构体.   不像tty_struct,   tty_driver 和 tty_ldisc 都是module 作者经常使用的东东~

对于数据流的读写


向串口写数据的时候。是非常直接的,中间没有buffer。可是从串口读数据就麻烦点了,并且这个时候中间是有buffer的。  数据被储存在buffer里面,知道用户空间的程序需求他们的时候,这个buffer里的数据就会传递到用户空间.

无论什么时候,当用户程序要从串口读数据的时候,这个时候buffer是空的,那么此时用户程序陷入休眠,直到buffer被填充。

注意到,write buffer 实际上也是存在的。仅仅是实现的非常直接,  由于write操作的每一步都由上一级驱动(注意图中的箭头是左边write操作是单向的。而右边read操作是双向的)。

read buffer 是放在struct tty 里面的。因为是动态分配的。所以不存在内存的浪费.

实际上,tty相关的buffer被分做两级管理: kernel developers chose to provide both a ``conventional'' buffer, where data is waiting to be eaten by the line discipline (i.e., in the default case, being
transferred to user space), and a ``flip'' buffer, used by hardware routines to store incoming data as quickly as possible, without the need to synchronize for concurrent access: flip buffers are exclusive ownership of the hardware device, which eventually
calls tty_flip_buffer_push to deliver data to the tty buffer, where the line discipline pulls it from.

It's interesting to note that the flip buffer is laid out as two physical buffers that are alternatively written to. This allows more reliable operation, as the interrupt handler will always have
a whole buffer available for writing. The function flush_to_ldisc, called by the low-level driver and part of the tty layer (i.e., tty_io.c), arranges for the flip buffer to be flipped, before the interrupt handler returns. This layout, by
the way, is why the flip buffer is called so.

register_serial  扮演的角色

假设你注意到 drivers/char/serial.c  导出了一个叫 register_serial 的函数,你会琢磨这家伙在 tty 构架中扮演什么角色呢?

其实,这个功能不过为了更便利easy的在执行时去加入标准的串口设备tty 驱动。


"《 Serial Drivers 》by Alessandro Rubini" 学习笔记的更多相关文章

  1. Data Types in the Kernel <LDD3 学习笔记>

    Data Types in the Kernel Use of Standard C Types /* * datasize.c -- print the size of common data it ...

  2. Introduction the naive“scull” 《linux设备驱动》 学习笔记

    Introduction the naive "scull" 首先.什么是scull? scull (Simple Character Utility for Loading Lo ...

  3. 初学c# -- 学习笔记(一)

    初学c# -- 学习笔记(一) 学习C#了,参考许多资料,一步步学习.这一段学习ajax的工作原理,参照其他例子写了web版的群聊小程序,全部文件代码也就不到300行,很简单.使用时先输入用户名,点确 ...

  4. .net学习笔记---xml基础知识

    一.XML简介 XML是一种标记语言,用于描述数据,它提供一种标准化的方式来来表示文本数据.XML文档以.xml为后缀.需要彻底注意的是XML是区分大小写的. 先从一个简单的XML例子来了解下xml基 ...

  5. XML学习笔记(二)-- DTD格式规范

    标签(空格分隔): 学习笔记 XML的一个主要目的是允许应用程序之间自由交换结构化的数据,因此要求XML文档具有一致的结构.业务逻辑和规则.可以定义一种模式来定义XML文档的结构,并借此验证XML文档 ...

  6. xml基础学习笔记01

    注意:刚刚看了网上对于XML中的标签,节点和元素?到底应该怎么表述?起初我也有这个疑惑,现在我的想法是:下面出现node的应称作节点,节点对象.element应称作元素,毕竟这更符合英文的本意.至于标 ...

  7. [转载]Log4net学习笔记

    Log4net 学习笔记: 主要是根据apache站点整理的: 原文链接:http://logging.apache.org/log4net/release/sdk/ http://logging.a ...

  8. Underscore.js 源码学习笔记(下)

    上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...

  9. 【MarkMark学习笔记学习笔记】javascript/js 学习笔记

    1.0, 概述.JavaScript是ECMAScript的实现之一 2.0,在HTML中使用JavaScript. 2.1 3.0,基本概念 3.1,ECMAScript中的一切(变量,函数名,操作 ...

随机推荐

  1. E - A strange lift 【数值型BFS+上下方向】

    There is a strange lift.The lift can stop can at every floor as you want, and there is a number Ki(0 ...

  2. RPD Volume 172 Issue 1-3 December 2016 评论04 end

    这一篇作为本期的结束是因为发现后面的一些基本上也是EPR有关的会议内容, Contribution of Harold M. Swartz to In VivoEPR and EPR Dosimetr ...

  3. 【贪心】Gym - 100507H - Pair: normal and paranormal

    每次取相邻的两个可以射击的从序列中删除,重复n次. 可以看作括号序列的匹配. #include<cstdio> #include<vector> using namespace ...

  4. python3 开发面试题(创建表结构)6.9

    纯sql语句写出: '''设计 图书管理系统 表结构: - 书 - 书名 - 作者 - 姓名 - 出版社 - 出版社名称 - 地址 一本书只能由一家出版社出版 --> 多对一(书对出版社) 一本 ...

  5. virtualenvwrapper的安装及问题解决

    安装virtualenvwrapperyum install python-setuptools python-develpip install virtualenvwrapper # linux下 ...

  6. JavaSE目录

    常识,环境变量,注释 标示符,常量,进制转换,类型转换,位运算符,语句 数组,函数 面向对象 多线程 String 包装类 集合 其他对象 IO流,IO流--FileReader&&F ...

  7. Java杂谈5——关键字final与volatile

    Final关键字 在Java语言中,随着语境的不同final关键字所代表的语义会有一些细微的差异.总的来说,final关键字表达的含义是“禁止修改”,这层有点类似于C++中的const关键字.之所以要 ...

  8. 在Delphi中使用键盘勾子获取键盘输入(译--5月7日)

    http://blog.sina.com.cn/s/blog_502b2e970100949s.html 获取键盘输入以控制无法接受输入焦点的控件考虑一些游戏,显示图片在TPainBox,但是TPai ...

  9. canvas如何兼容IE8

    大家都知道canvas是个非常好玩的东西,但是IE9以下的浏览器不支持,有时候业务需求必须用到canvas,且又要求兼容IE8浏览器,那怎么办呢? 1.添加对html5的支持:<!--[if I ...

  10. httpd 服务的两个节点的HA

    实验目的是:实现两个节点的http和nfs服务的HA集群. 实现条件:准备两个节点.node1,node2作为HA1,HA2提供集群服务.在node1和node2分别按照httpd服务.挂载nfs服务 ...