嵌入式驱动解析:从串口驱动到Linux驱动模型
本文通过对Linux下串口驱动的分析。由最上层的C库。到操作系统系统调用层的封装。再到tty子系统的核心。再到一系列线路规程。再到最底层的硬件操作。
对Linux中的tty子系统进行简要的说明。从理论到实践。以便读者能对OS原理有更深入的理解和更具体的掌握。
在具体分析之前。我们必需对串口。驱动。和Linux操作系统有肯定的理解。这一阶段我们有三个问题需要处理:
1.什么是Linux操作系统。
2.什么是Linux设施驱动。
3.关于串口的种种。
要理解这些概念。如下我详情了一点这方面的知识。不过遗憾的是对少量概念有着不可避免的向前引用。
这个过程中我会尽量忽略次要因素。以在本次调研中最主要目的为主线。假如读者您对这些概念已经有很深入的了解。可以直接阅读后面的代码分析:
1、什么是Linux操作系统 ?
Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多客户、多任务、支持多线程和多CPU的操作系统。
它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳固的多客户网络操作系统。
Linux操作系统诞生于1991 年10 月5 日(这是第一次正式向外公布时间)。Linux存在着许多不同的Linux版本,但它们都使用了Linux内核。
Linux具有惊人的可移植性。可安装在各种计算机硬件设施中,比方手机、平板电脑、路由器、视频游戏控制台、台式计算机、大型机和超级计算机。
严格来讲,Linux这个词本身只表示Linux内核,但实际上人们已经习惯了用Linux来描述整个基于Linux内核,并且使用GNU 工程各种工具和数据库的操作系统。
在这几个简要的段落中。有不少新的名词被引入了进来。下面我对几个重要的概念进行形容。
A、关于类UNIX系统
类Unix系统(英文:Unix-like)指各种传统的Unix系统(比方FreeBSD、OpenBSD、SUN公司的Solaris)以及各种与传统Unix相似的系统(例如Minix、Linux、QNX等)。
它们尽管有的是自由软件,有的是商业软件,但都相当程度地继承了原始UNIX的特性,有许多类似处,并且都在肯定程度上遵守POSIX规范。
这个在少量经典的操作系统教科书中已经作了说明。我们仅需知道。它和我们熟知的Windows系列操作系统一样。都是一种现代操作系统。对底层的计算机资源进行笼统。对上层客户提供调用接口。完成计算机应该完成的功能。
B、关于可移植性
可移植性指与软件从某一环境转移到另一环境下的难易程度。为取得较高的可移植性,在设计过程中常采用通用的程序设计语言和运行支撑环境。尽量不用与系统的底层相关性强的语言。
可移植性是软件质量之一,良好的可移植性可以提高软件的生命周期。代码的可移植性主题是软件;可移植性是软件产品的一种能力属性,其行为体现为一种程度,而体现出来的程度与环境密切相关。
一个操作系统的可移植性往往体现在它能在运行在不同的体系结构上。感性的了解就是可以支持的设施有很多。比方前文所说的,Linux可以运行在大型服务器上。各种平板电脑上。
前段时间有黑客成功的把Linux移植到一个佳能照相机上。并且在这个照相机上运行了少量主流的软件。可以说。只需有足够可以利用的硬件资源。即可以把Linux移植到这个硬件平台上去。这个资源的最低要求往往很低。这可以与对硬件资源要求很高的Windows有一个鲜明的比照。举个例子就是。当Windows 10的更新提醒从你计算机的右下角弹出时。
你可以不假思索的点击‘马上更新’吗?我想大多数人对这个问题的答案能否定的。为什么?由于大多数情况下。更新之后就会变得更卡。推迟更大。少量无用而庞大的软件疯狂的占用你有限的计算机资源。而假如你选择的是Linux。你几乎可以任意的在计算机上安装软件。运行程序(假如你的内存不是太小。且硬盘交换分区足够的话)。
Linux核心已经将有限的硬件资源发挥到了极致。开源软件良好的模块化设计在各个层次上充分利用了程序的局部性原理。(当然这是在损失了肯定易用性的前提下的。)。不好心思我扯远了。这些不是本文的重点。。
因为笔者没有土豪到有很多计算机。所以选择了一款比较便宜的ARM9开发板作为开发平台。它的CPU是三星公司生产的S3C2440。核心是ARM920T。
C、关于Linux的基本思想
Linux的基本思想有两点:
第一. 一切都是文件。系统中的所有都归结为一个文件,包括命令、硬件和软件设施、操作系统、进程等等对于操作系统内核而言,都被视为拥有各自特性或者类型的文件。至于说Linux是基于Unix的,很大程度上也是由于这两者的基本思想十分相近
第二. 每个软件都有确定的用途。
D、关于Linux的特点
完全免费
Linux是一款免费的操作系统,客户可以通过网络或者其余途径免费取得,并可以任意修改其源代码。这是其余的操作系统所做不到的。
正是因为这一点,来自全世界的无数程序员参加了Linux的修改、编写工作,程序员可以根据自己的兴趣和灵感对其进行改变,这让Linux吸收了无数程序员的精华,不断壮大。
完全兼容POSIX1.0标准
这使得可以在Linux下通过相应的模拟器运行常见的DOS、Windows的程序。这为客户从Windows转到Linux奠定了基础。
许多客户在考虑使用Linux时,就想到以前在Windows下常见的程序能否能正常运行,这一点就消除了他们的疑虑。
多客户、多任务
Linux支持多客户,各个客户对于自己的文件设施有自己特殊的权利,保证了各客户之间互不影响。多任务则是现在电脑最主要的一个特点,Linux可以使多个程序同时并独立地运行。
良好的界面
Linux同时具备字符界面和图形界面。在字符界面客户可以通过键盘输入相应的指令来进行操作。它同时也提供了相似Windows图形界面的X-Window系统,客户可以使用鼠标对其进行操作。在X-Window环境中就和在Windows中类似,可以说是一个Linux版的Windows。
支持多种平台
Linux可以运行在多种硬件平台上,如具备x86、680×0、SPARC、Alpha等解决器的平台。此外Linux还是一种嵌入式操作系统,可以运行在掌上电脑、机顶盒或者游戏机上。2001年1月份发布的Linux 2.4版内核已经能够完全支持Intel 64位芯片架构。同时Linux也支持多解决器技术。多个解决器同时工作,使系统性能大大提高。
文件类型
普通文件(regular file):就是一般存取的文件,由ls-al显示出来的属性中,第一个属性为 [-],例如 [-rwxrwxrwx]。另外,依照文件的内容,又大致可以分为:
1、纯文本文件(ASCII):这是Unix系统中最多的一种文件类型,之所以称为纯文本文件,是由于内容可以直接读到的数据,例如数字、字母等等。设置文件几乎都属于这种文件类型。举例来说,使用命令“cat ~/.bashrc”即可以看到该文件的内容(cat是将文件内容读出来)。
2、二进制文件(binary):系统其实仅认识且可以执行二进制文件(binary file)。Linux中的可执行文件(脚本,文本方式的批解决文件不算)就是这种格式的。举例来说,命令cat就是一个二进制文件。
3、数据格式的文件(data):有些程序在运行过程中,会读取某些特定格式的文件,那些特定格式的文件可以称为数据文件(data file)。举例来说,Linux在客户登入时,都会将登录数据记录在 /var/log/wtmp文件内,该文件是一个数据文件,它能通过last命令读出来。但使用cat时,会读出乱码。由于它是属于一种特殊格式的文件。
4、目录文件(directory):就是目录,第一个属性为[d],例如 [drwxrwxrwx]。
连接文件(link):相似Windows下面的快捷方式。第一个属性为 [l],例如 [lrwxrwxrwx]。
5、设施与设施文件(device):与系统外设及存储等相关的少量文件,通常都集中在 /dev目录。通常又分为两种:
块设施文件:就是存储数据以供系统存取的接口设施,简单而言就是硬盘。例如一号硬盘的代码是 /dev/hda1等文件。第一个属性为 [b]。
字符设施文件:即串行端口的接口设施,例如键盘、鼠标等等。第一个属性为 [c]。
6、套接字(sockets):这类文件通常用在网络数据连接。可以启动一个程序来监听用户端的要求,用户端即可以通过套接字来进行数据通信。第一个属性为 [s],最常在 /var/run目录中看到这种文件类型。
7、管道(FIFO,pipe):FIFO也是一种特殊的文件类型,它主要的目的是,处理多个程序同时存取一个文件所造成的错误。FIFO是first-in-first-out(先进先出)的缩写。第一个属性为 [p]
文件结构
/:根目录,所有的目录、文件、设施都在/之下,/就是Linux文件系统的组织者,也是最上级的领导者。
/bin:bin 就是二进制(binary)英文缩写。在一般的系统当中,都可以在这个目录下找到linux常用的命令。系统所需要的那些命令位于此目录。
/boot:Linux的内核及引导系统程序所需要的文件目录,比方 vmlinuz initrd.img 文件都位于这个目录中。在一般情况下,GRUB或者LILO系统引导管理器也位于这个目录。
/cdrom:这个目录在刚刚安装系统的时候是空的。可以将光驱文件系统挂在这个目录下。例如:mount /dev/cdrom /cdrom
/dev:dev 是设施(device)的英文缩写。这个目录对所有的客户都十分重要。由于在这个目录中包含了所有linux系统中使用的外部设施。但是这里并不是放的外部设施的驱动程序。这一点和常用的windows,dos操作系统不一样。它实际上是一个访问这些外部设施的端口。可以非常方便地去访问这些外部设施,和访问一个文件,一个目录没有任何区别。
/etc:etc这个目录是linux系统中最重要的目录之一。在这个目录下存放了系统管理时要用到的各种配置文件和子目录。要用到的网络配置文件,文件系统,x系统配置文件,设施配置信息,设置客户信息等都在这个目录下。
/home:假如建立一个客户,客户名是”xx”,那么在/home目录下就有一个对应的/home/xx路径,用来存放客户的主目录。
/lib:lib是库(library)英文缩写。这个目录是用来存放系统动态连接共享库的。几乎所有的应用程序都会用到这个目录下的共享库。因而,千万不要轻易对这个目录进行什么操作,一旦发生问题,系统就不能工作了。
/lost+found:在ext2或者ext3文件系统中,当系统意外崩溃或者机器意外关机,而产生少量文件碎片放在这里。当系统启动的过程中fsck工具会检查这里,并修复已经损坏的文件系统。有时系统发生问题,有很多的文件被移到这个目录中,可能会用手工的方式来修复,或者移到文件到原来的位置上。
/mnt:这个目录一般是用于存放挂载储存设施的挂载目录的,比方有cdrom等目录。可以参看/etc/fstab的定义。
/media:有些linux的发行版使用这个目录来挂载那些usb接口的移动硬盘(包括U盘)、CD/DVD驱动器等等。
/opt:这里主要存放那些可选的程序。
/proc:可以在这个目录下获取系统信息。这些信息是在内存中,由系统自己产生的。
/root:Linux超级权限客户root的家目录。
/sbin:这个目录是用来存放系统管理员的系统管理程序。大多是涉及系统管理的命令的存放,是超级权限客户root的可执行命令存放地,普通客户无权限执行这个目录下的命令,这个目录和/usr/sbin; /usr/X11R6/sbin或者/usr/local/sbin目录是类似的,但凡目录sbin中包含的都是root权限才能执行的。
/selinux :对SElinux的少量配置文件目录,SElinux可以让linux更加安全。
/srv 服务启动后,所需访问的数据目录,举个例子来说,www服务启动读取的网页数据即可以放在/srv/www中
/tmp:临时文件目录,用来存放不同程序执行时产生的临时文件。有时客户运行程序的时候,会产生临时文件。/tmp就用来存放临时文件的。/var/tmp目录和这个目录类似。
/usr:这是linux系统中占用硬盘空间最大的目录。客户的很多应用程序和文件都存放在这个目录下。在这个目录下,可以找到那些不适合放在/bin或者/etc目录下的额外的工具
/usr/local:这里主要存放那些手动安装的软件,即不是通过“新立得”或者apt-get安装的软件。它和/usr目录具备相相似的目录结构。让软件包管理器来管理/usr目录,而把自己设置的脚本(scripts)放到/usr/local目录下面、。
/usr/share :系统共用的东西存放地,比方/usr/share/fonts 是字体目录,/usr/share/doc和/usr/share/man帮助文件。
/var:这个目录的内容是经常变动的,看名字就知道,可以了解为vary的缩写,/var下有/var/log 这是用来存放系统日志的目录。/var/ www目录是定义Apache服务器站点存放目录;/var/lib 用来存放少量库文件,比方MySQL的,以及MySQL数据库的的存放地。
如上。相信读者已经对Linux操作系统有了一个概观。对于少量具体命令。笔者决定需要用到的时候再做说明。现在我们来看看第二个概念:
2、什么是Linux设施驱动
设施驱动最浅显的解释就是驱使硬件设施行动。驱动与底层硬件直接打交道,按照硬件设施的具体工作方式,读写设施的寄存器,完成设施的轮询、中断解决、DMA通信,进行物理内存向虚拟内存的映射等,最终让通信设施能收发数据,让显示设施能显示文字和画面,让存储设施能记录文件和数据。
Linux设施驱动是对底层硬件资源的笼统。对上层的操作系统其余服务提供一个良好的接口。让其余服务可以把一个特定的硬件。或者是一种机制当做一个文件使用。使用通用的系统调用进行调用。
3、关于串口的种种
众所周知。我们现在的计算机上面有很多接口。如USB。网口。并口等。串口总线是其中的一个。串行接口简称串口,也称串行通信接口或者串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。
串行接口 (Serial Interface) 是指数据一位一位地顺序传送,其特点是通信线路简单,只需一对传输线即可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。一条信息的各位数据被逐位按顺序传送的通讯方式称为串行通讯。
串行通讯的特点是:数据位的传送,按位顺序进行,最少只要一根传输线就可完成;成本低但传送速度慢。串行通讯的距离可以从几米到几千米;根据信息的传送方向,串行通讯可以进一步分为单工、半双工和全双工三种。
串口通信的两种最基本的方式:同步串行通信方式和异步串行通信方式。
同步串行是指SPI(SerialPeripheral interface)的缩写,顾名思义就是串行外围设施接口。SPI总线系统是一种同步串行外设接口,它可以使MCU与各种外围设施以串行方式进行通信以交换信息,TRM450是SPI接口。
异步串行是指UART(UniversalAsynchronous Receiver/Transmitter),通用异步接收/发送。UART是一个并行输入成为串行输出的芯片,通常集成在主板上。UART包含TTL电平的串口和RS232电平的串口。
TTL电平是3.3V的,而RS232是负逻辑电平,它定义+5+12V为低电平,而-12-5V为高电平,MDS2710、MDS SD4、EL805等是RS232接口,EL806有TTL接口。
串行接口按电气标准及协议来分包括RS-232-C、RS-422、RS485等。
RS-232
也称标准串口,最常用的一种串行通讯接口。它是在1970年由美国电子工业协会(EIA)联合贝尔系统、调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。
它的全名是“数据终端设施(DTE)和数据通讯设施(DCE)之间串行二进制数据交换接口技术标准”。传统的RS-232-C接口标准有22根线,采用标准25芯D型插头座(DB25),后来使用简化为9芯D型插座(DB9),现在应用中25芯插头座已很少采用。
RS-232采取不平衡传输方式,即所谓单端通讯。因为其发送电平与接收电平的差仅为2V至3V左右,所以其共模抑制能力差,再加上双绞线上的分布电容,其传送距离最大为约15米,最高速率为20kb/s。RS-232是为点对点(即只用一对收、发设施)通讯而设计的,其驱动器负载为3~7kΩ。所以RS-232适合本地设施之间的通信。
RS-422
标准全称是“平衡电压数字接口电路的电气特性”,它定义了接口电路的特性。典型的RS-422是四线接口。实际上还有一根信号地线,共5根线。其DB9连接器引脚定义。因为接收器采用高输入阻抗和发送驱动器比RS232更强的驱动能力,故允许在相同传输线上连接多个接收节点,最多可接10个节点。
即一个主设施(Master),其他为从设施(Slave),从设施之间不能通信,所以RS-422支持点对多的双向通信。接收器输入阻抗为4k,故发端最大负载能力是10×4k+100Ω(终接电阻)。
RS-422四线接口因为采用单独的发送和接收通道,因而不必控制数据方向,各装置之间任何必需的信号交换均可以按软件方式(XON/XOFF握手)或者硬件方式(一对单独的双绞线)实现。
RS-422的最大传输距离为1219米,最大传输速率为10Mb/s。其平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能达到最大传输距离。只有在很短的距离下才能取得最高速率传输。一般100米长的双绞线上所能取得的最大传输速率仅为1Mb/s。
RS-485
是从RS-422基础上发展而来的,所以RS-485许多电气规定与RS-422相仿。如都采用平衡传输方式、都需要在传输线上接终接电阻等。RS-485可以采用二线与四线方式,二线制可实现真正的多点双向通信,而采用四线连接时,与RS-422一样只能实现点对多的通信,即只能有一个主(Master)设施,其他为从设施,但它比RS-422有改进,无论四线还是二线连接方式总线上可多接到32个设施。
RS-485与RS-422的不同还在于其共模输出电压是不同的,RS-485是-7V至+12V之间,而RS-422在-7V至+7V之间,RS-485接收器最小输入阻抗为12kΩ、RS-422是4kΩ;因为RS-485满足所有RS-422的规范,所以RS-485的驱动器可以在RS-422网络中应用。
RS-485与RS-422一样,其最大传输距离约为1219米,最大传输速率为10Mb/s。平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能使用规定最长的电缆长度。只有在很短的距离下才能取得最高速率传输。一般100米长双绞线最大传输速率仅为1Mb/s。
笔者采用的RS-232串口通信协议。下面对其通信接线方法做简要说明。目前较为常用的串口有9针串口(DB9)和25针串口(DB25),通信距离较近时(<12m),可以用电缆线直接连接标准RS232端口(RS422,RS485较远),若距离较远,需附加调制解调器(MODEM)或者其余相关设施。最为简单且常用的是三线制接法,即地、接收数据和发送数据三脚相连,这是最为基本的接法,且直接用RS232相连。
上面是对微机标准串行口而言的,还有许多非标准设施,不做说明。
好了。到此为止我们已经处理了一开始的三个问题。让我们进入实际的代码。实际的硬件来进行分析。
在一个硬件平台上。硬件是可用的。我们必需要烧写适当的软体到平台的RAM中。这样CPU才能跳转到最先的指令。而后慢慢加载各种资源。才能完成系统的自举。
一般我们采用BootLoader进行硬件的初始化。并引导至操作系统核心。
笔者采用的BootLoader是u-Boot-1.1.16。Uboot是一个众所周知的开源软件。读者仅需理解它起到了BootLoader的作用就可。这里不多做解释。仅对串口的连接和程序的下载作简要说明:
image-20201102211132964
将UBOOT目录下的u-boot.bin下载到开发平台上。在Windows打开设施管理器。选择端口。从而找到正确的com口号。在此之前确保开发板的串口与笔记本的USB口连接。(由于现在笔记本都没有并口了。所以只能采用USB转串口线。搭配开发板上的电平转换芯片来完成串口连接目的。)
image-20201102211256892
而后我们再使用一个工具。即SecureCRT。找到对应的com号。完成快速链接。波特率选择115200。取消流控。
image-20201102211343319
假如一切顺利。在笔记本上即可以看到串口的相似下面的输出。这就是传说中的串口控制台。
image-20201102211435199
这个串口的指令功能是由Uboot本身完成的。并不是linux下的串口驱动。
引入此图旨在让读者感性的认识到串口控制台的功能是什么。
下面正式开始对串口打开。发送。接收函数的分析。这里向前引用一个函数。就是linux内核中几种2440芯片通用的串口发送函数s3c24xx_serial_start_tx。函数公告为static voids3c24xx_serial_start_tx(struct uart_port *port):函数定义在./linux/driver/tty/serial/samsung.c中。
好了。我们从这个目录结构开始。说明大概的tty子系统驱动模型。
首先。最前面的linux是内核代码的根目录。如图所示:
image-20201102211537790
至此。我们面临一个问题。linux内核是什么。
Linux内核是什么?
Linux是一种开源电脑操作系统内核。它是一个用C语言写成,符合POSIX标准的类Unix操作系统。
Linux最早是由芬兰黑客Linus Torvalds为尝试在英特尔x86架构上提供自由免费的类Unix操作系统而开发的。该计划开始于1991年,在计划的早期有少量Minix 黑客提供了协助,而今天全球无数程序员正在为该计划无偿提供帮助。
Linux是一个一体化内核(monolithickernel)系统。“内核”指的是一个提供硬件笼统层、磁盘及文件系统控制、多任务等功能的系统软件。一个内核不是一套完整的操作系统。
一套基于Linux内核的完整操作系统叫作Linux操作系统,或者是GNU/Linux。设施驱动程序可以完全访问硬件。Linux内的设施驱动程序可以方便地以模块化(modularize)的形式设置,并在系统运行期间可直接装载或者卸载。
操作系统是一个用来和硬件打交道并为客户程序提供一个有限服务集的低级支撑软件。一个计算机系统是一个硬件和软件的共生体,它们互相依赖,不可分割。计算机的硬件,含有外围设施、解决器、内存、硬盘和其余的电子设施组成计算机的发动机。但是没有软件来操作和控制它,自身是不能工作的。
完成这个控制工作的软件就称为操作系统,在Linux的术语中被称为“内核”,也可以称为“核心”。Linux内核的主要模块(或者组件)分以下几个部分:存储管理、CPU和进程管理、文件系统、设施管理和驱动、网络通信,以及系统的初始化(引导)、系统调用等。
系统调用接口
SCI 层提供了某些机制执行从客户空间到内核的函数调用。正如前面探讨的一样,这个接口依赖于体系结构,甚至在相同的解决器家族内也是如此。SCI 实际上是一个非常有用的函数调用多路复用和多路分解服务。在 ./linux/kernel 中您可以找到 SCI 的实现,并在 ./linux/arch 中找到依赖于体系结构的部分。
进程管理
进程管理的重点是进程的执行。在内核中,这些进程称为线程,代表了单独的解决器虚拟化(线程代码、数据、堆栈和 CPU寄存器)。在客户空间,通常使用进程这个术语,不过 Linux 实现并没有区分这两个概念(进程和线程)。
内核通过 SCI 提供了一个应用程序编程接口(API)来创立一个新进程(fork、exec 或者 Portable Operating System Interface [POSⅨ] 函数),中止进程(kill、exit),并在它们之间进行通信和同步(signal 或者者 POSⅨ机制)。
进程管理还包括解决活动进程之间共享 CPU的需求。内核实现了一种新型的调度算法,不论有多少个线程在竞争 CPU,这种算法都可以在固定时间内进行操作。这种算法就称为 O⑴调度程序,这个名字就表示它调度多个线程所使用的时间和调度一个线程所使用的时间是相同的。
O⑴调度程序也可以支持多解决器(称为对称多解决器或者 SMP)。您可以在 ./linux/kernel 中找到进程管理的源代码,在 ./linux/arch 中可以找到依赖于体系结构的源代码。
内存管理
内核所管理的另外一个重要资源是内存。为了提高效率,假如由硬管理虚拟内存,内存是按照所谓的内存页方式进行管理的(对于大部分体系结构来说都是 4KB)。Linux 包括了管理可用内存的方式,以及物理和虚拟映射所使用的硬件机制。
不过内存管理要管理的可不止 4KB缓冲区。Linux 提供了对 4KB缓冲区的笼统,例如 slab 分配器。这种内存管理模式使用 4KB缓冲区为基数,而后从中分配结构,并跟踪内存页使用情况,比方哪些内存页是满的,哪些页面没有完全使用,哪些页面为空。这样就允许该模式根据系统需要来动态调整内存使用。
为了支持多个客户使用内存,有时会出现可用内存被消耗光的情况。因为这个起因,页面可以移出内存并放入磁盘中。这个过程称为交换,由于页面会被从内存交换到硬盘上。内存管理的源代码可以在 ./linux/mm 中找到。
虚拟文件系统
虚拟文件系统(VFS)是 Linux 内核中非常有用的一个方面,由于它为文件系统提供了一个通用的接口笼统。VFS 在 SCI 和内核所支持的文件系统之间提供了一个交换层。
VFS 在客户和文件系统之间提供了一个交换层
在 VFS 上面,是对诸如 open、close、read 和 write 之类的函数的一个通用 API 笼统。在 VFS 下面是文件系统笼统,它定义了上层函数的实现方式。它们是给定文件系统(超过 50 个)的插件。文件系统的源代码可以在 ./linux/fs 中找到。
文件系统层之下是缓冲区缓存,它为文件系统层提供了一个通用函数集(与具体文件系统无关)。这个缓存层通过将数据保留一段时间(或者者随即预先读取数据以便在需要是即可用)优化了对物理设施的访问。缓冲区缓存之下是设施驱动程序,它实现了特定物理设施的接口。
好了。相信读者已经对linuxkernel 有了一个概观。下面我们继续分析这个路径背后代表的模型结构。(./linux/driver/tty/serial/samsung.c)
driver是驱动程序的目录。如图所示:
image-20201102211620361
前文对linux设施驱动程序有了一个大概的形容。下面我们具体看一下linux下的驱动。
纵览linux/drivers目录,大概还有35个以上的子目录,每个子目录基本上就代表了一种设施驱动,有atm、block、char、misc、input、net、usb、sound、video等。这里只形容在嵌入式系统里面用得最为广泛的3种设施。
1.字符设施(char device)
字符设施是Linux最简单的设施,可以像文件一样访问。初始化字符设施时,它的设施驱动程序向Linux登记,并在字符设施向量表中添加一个device_struct数据结构条目,这个设施的主设施标识符用做这个向量表的索引。
一个设施的主设施标识符是固定的。chrdevs向量表中的每一个条目,一个device_struct数据结构,包括两个元素:一个登记设施驱动程序名称的指针和一个指向一组文件操作的指针。可以参考的代码是include/linux/ major.h。
一般来说像鼠标、串口、键盘等设施都属于字符设施。
2.块设施(block device)
块设施是文件系统的物质基础,它也可以像文件一样被访问。Linux用blkdevs向量表维护已经登记的块设施文件。它像chrdevs向量表一样,使用设施的主设施号作为索引。它的条目也是device_struct数据结构。与字符设施不同的是,块设施分为SCSI类和IDE类。
向Linux内核登记并向核心提供文件操作。一种块设施类的设施驱动程序向这种类提供和类相关的接口。可以参考的代码是fs/devices.c。
每一个块设施驱动程序必需提供普通的文件操作接口和对于buffer cache的接口。每一个块设施驱动程序填充blk_dev向量表中的blk_dev_struct数据结构。此向量表的索引是设施的主设施号。其中blk_dev_struct数据结构包括一个请求例程的地址和一个指针,指向一个request数据结构的列表,每一个都表达buffer cache向设施读/写一块数据的一个请求。
可以参考的源代码是drivers/block/ll_rw_blk.c和include/linux/blkdev.h。
当buffer cache从一个已登记的设施读/写一块数据,或者者希望读、写一块数据到其余位置时,就在blk_dev_struct中添加一个request数据结构。每个request数据结构都有一个指向一个或者多个buffer_head数据结构的指针,每一个都是读/写一块数据的请求。
假如buffer_head数据结构被锁定(buffer_cache),可能会有一个进程在等待这个缓冲区的阻塞进程完成。每一个request数据结构都是从all_request表中分配的。假如request添加到空的request列表中,就调用驱动程序的request函数解决这个request队列,否则驱动程序只是简单地解决request队列中的每一个请求。
块设施驱动程序和字符设施驱动程序的主要区别是:在对字符设施发出读、写请求时,实际的硬件I/O一般紧接着就发生了,块设施则不然,它利用一块系统内存作为缓冲区,当客户进程对设施请求能满足客户的要求时,就返回请求的数据,假如不能就调用请求函数来进行实际的I/O操作。块设施是主要针对磁盘等慢速设施的,以免耗费过多的CPU时间来等待。
块设施主要有硬盘、光盘驱动器等。可以查看文件/proc/devices取得。
3.网络设施(net device)
网络设施在系统中的作用相似于一个已挂载的块设施。块设施将自己注册到blk_dev数据及其余内核结构中,而后通过自己的request函数在发生请求时传输和接收数据块,同样网络设施也必需在特定的数据结构中注册自己,以便与外界交换数据包时被调用。网络设施在Linux里做专门的解决。Linux的网络系统主要是基于BSD UNIX的Socket机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统里支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。
4.杂项设施(misc device)
杂项设施也是在嵌入式系统中用得比较多的一种设施驱动,在第11章里面详情的sub LCD和弦芯片的驱动等都是采用 misc device 的驱动方式实现的。在 Linux 内核的include\linux目录下有Miscdevice.h文件,要把自己定义的misc device从设施定义在这里。其实是由于这些字符设施不符合预先确定的字符设施范畴,所有这些设施采用主编号10,一起归于misc device,其实misc_register就是用主标号10调用register_chrdev()的。
这是driver目录下的分类。我们主要调研的串口驱动。属于TTY子系统。所以我们cd到tty目录下。ls显示里面的文件。如图所示:
image
下面对linux内核tty设施做一点简要说明。
tty一词源于Teletypes,或者Teletypewriters,它是最早出现的一种终端设施,相似电传打字机,由Teletype公司生产。最初tty是指连接到Unix系统上的物理或者者虚拟终端。终端是一种字符型设施,通常使用tty来统称各种类型的终端设施。随着时间的推移,当通过串行口能够建立起终端连接后,这个名字也用来指任何的串口设施。
它还有多种类,例如串口(ttySn、ttySACn、ttyOn)、USB到串口的转换器(ttyUSBn),还有需要特殊解决才能正常工作的调制解调器(比方传统的WinModem类设施)等。tty虚拟设施支持虚拟控制台,它能通过键盘及网络连接或者者通过xterm会话登录到计算机上。
其实起初终端和控制台都不是个人电脑的概念,而是多人共用的小型中型大型计算机上的概念。终端为主机提供了人机接口,每个人都通过终端使用主机的资源。终端有字符终端和图形终端两种。一台主机可以连很多终端。控制台是一种特殊的人机接口, 是人控制主机的第一人机接口。
而主机对于控制台的信任度高于其余终端。对此还可以结合内核启动代码中init进程打开/dev/console和执行两次sys_dup(0),以及标准输入、标准输出、标准出错,还有就是进程fork后的标准输入输出的复制情况来一起了解。而个人计算机只有控制台,没有终端。当然愿意的话,可以在串口上连一两台字符哑终端。
但是linux按POSIX标准把个人计算机当成小型机来用,在控制台上通过getty软件虚拟了六个字符哑终端(或者者叫虚拟控制台终端tty1-tty6)(数量可以在/etc/inittab里自己调整)和一个图型终端, 在虚拟图形终端中又可以通过软件(如rxvt)再虚拟无限多个伪终端(pts/0等)。
但这全是虚拟的,尽管用起来一样,但实际上没有物理实体。所以在个人计算机上,只有一个实际的控制台,没有终端,所有终端都是在控制台上用软件模拟的。要把个人计算机当主机再通过串口或者网卡外连真正的物理终端也可以,论成本,呵呵。谁会怎样做呢。
终端按照其自身能力分类,可以分为:
1、哑终端(瘦用户端)
早期的计算机终端是通过串行RS-232通信的,它只能解释有限数量的控制码(CR,LF等),但没有能力解决执行特殊的转义序列功能(如清行、清屏或者控制光标的位置)。简单来说就是解决能力有限的终端机,他们一般基本上只具备和机械电传打字机相似的有限功能。这种类型的终端称为哑终端。
现在依然在现代类Unix系统上得到支持,通过设置环境变量TERM=dumb。哑终端有时用来指任何类型的通过RS-232连接的传统计算机终端,不对数据进行本地解决或者本地执行客户程序的串行通信终端。哑终端有时也指功能有限,只有单色文本解决能力或者直接传输每一个键入的字符而不等待主机轮询的公共计算机终端。
2、智能终端(胖用户端)
智能终端就是有能力解决转义序列,也就是说解决能力较强的终端机。
Linux系统的终端设施一般有以下几种:
1、 控制台
系统控制台/dev/console
/dev/console是系统控制台,是与操作系统交互的设施。系统所产生的信息会发送到该设施上。平常我们看到的PC只有一个屏幕和键盘,它其实就是控制台。目前只有在单客户模式下,才允许客户登录控制台/dev/console。(可以在单客户模式下输入tty命令进行确认)。
console有缓冲的概念,为内核提供打印输出。内核把要打印的内容装入缓冲区__log_buff,而后由console来决定打印到哪里(比方是tty0还是ttySn等)。console指向激活的终端。历史上,console指主机本身的屏幕和键盘,而tty指用电缆链接的其它位置的控制台。
某些情况下console和tty0是一致的,就是当前所使用的是虚拟终端,也是激活虚拟终端。所以有些资料中称/dev/console是到/dev/tty0的符号链接,但是这样说现在看来是不对的:根据内核文档,在2.1.71之前,/dev/console根据不同系统设定,符号链接到/dev/tty0或者者其余tty*上,在2.1.71版本之后则完全由内核代码内部控制它的映射。
假如一个终端设施要实现console功能,必需向内核注册一个struct console结构,一般的串口驱动中都会有。假如设施要实现tty功能,必需要内核的tty子系统注册一个struct tty_driver结构,注册函数在drivers/tty/tty_io.c中。一个设施可以同时实现console和tty_driver,一般串口都这么做。
当前控制台:/dev/tty
这是应用程序中的概念,假如当前进程有控制终端(Controlling Terminal),那么/dev/tty就是当前进程控制台的设施文件。对于你登录的shell,/dev/tty就是你使用的控制台,设施号是(5,0)。不过它并不指任何物理意义上的控制台,/dev/tty会映射到当前设施(使用命令“tty”可以查看它具体对应哪个实际物理控制台设施)。输出到/dev/tty的内容只会显示在当前工作终端上(无论是登录在ttyn中还是pty中)。
你假如在控制台界面下(即字符界面下)那么dev/tty就是映射到dev/tty1-6之间的一个(取决于你当前的控制台号),但是假如你现在是在图形界面(Xwindows),那么你会发现现在的/dev/tty映射到的是/dev/pts的伪终端上。/dev/tty有些相似于到实际所使用终端设施的一个联接。
你可以输入命令“tty”,将显示当前映射终端如:/dev/tty1或者者/dev/pts/0等。也可以使用命令“ps -ax”来查看其余进程与哪个控制终端相连。
在当前终端中输入 echo “tekkaman” > /dev/tty ,都会直接显示在当前的终端中。
虚拟控制台 /dev/ttyn
/dev/ttyn是进程虚拟控制台,他们共享同一个真实的物理控制台。假如在进程里打开一个这样的文件且该文件不是其余进程的控制台时,那该文件就是这个进程的控制台。
进程printf数据会输出到这里。在PC上,客户可以使用alt+Fn切换控制台,现在不知道怎样回事我用Ctrl + Alt + Fn才能切换。这没具体看过为啥。可能是Linux没有继承UNIX这方面的传统罢了。看起来感觉存在多个屏幕,这种虚拟控制台对应tty1~n,其中:
/dev/tty1等代表第一个虚拟控制台
例如当使用ALT+F2进行切换时,系统的虚拟控制台为/dev/tty2 ,当前控制台(/dev/tty)则指向/dev/tty2
在UNIX系统中,计算机显示器通常被称为控制台(Console)。它仿真了类型为Linux的一种终端,并且有少量设施特殊文件与之相关联:tty0、tty1、tty2等。当你在控制台上登录时,使用的是tty1。使用Alt+[F1—F6]组合键时,我们即可以切换到tty2、tty3等上面去。
读者可以登录到不同的虚拟控制台上去,因此可以让系统同时有几个不同的会话存在。
而比较特殊的是/dev/tty0,他代表当前虚拟控制台,其实就是当前所使用虚拟控制台的一个别名。因而不论当前正在使用哪个虚拟控制台(注意:这里是虚拟控制台,不包括伪终端),系统信息都会重定位到/dev/tty0上。
只有系统或者超级客户root可以向/dev/tty0进行写操作。tty0是系统自动打开的,但不用于客户登录。在Framebuffer设施没有启用的系统中,可以使用/dev/tty0访问显卡。
2、 伪终端pty(pseudo-tty)
伪终端(Pseudo Terminal)是终端的发展,为满足现在需求(比方网络登陆、xwindow窗口的管理)。它是成对出现的逻辑终端设施(即master和slave设施, 对master的操作会反映到slave上)。它多用于模拟终端程序,是远程登陆(telnet、ssh、xterm等)后创立的控制台设施。
历史上,有两套伪终端软件接口:
BSD接口:较简单,master为/dev/pty [p-za-e] [0-9a-f];slave为 /dev/tty [p-za-e] [0-9a-f] ,它们都是配对的出现的。例如/dev/ptyp3和/dev/ttyp3。但因为在编程时要找到一个合适的终端需要一一尝试,所以逐步被放弃。
Unix 98接口:使用一个/dev/ptmx作为master设施,在每次打开操作时会得到一个master设施fd,并在/dev/pts/目录下得到一个slave设施(如 /dev/pts/3和/dev/ptmx),这样就避免了一一尝试的麻烦。
因为可能有好几千个客户登陆,所以/dev/pts/* 是动态生成的,不象其余设施文件是构建系统时就已经产生的硬盘节点(假如未使用devfs、udev、mdev等) 。第一个客户登陆,设施文件为/dev/pts/0,第二个为/dev/pts/1,以此类推。它们并不与实际物理设施直接相关。现在大多数系统是通过此接口实现pty。
我们在X Window下打开的终端或者使用telnet或者ssh等方式登录Linux主机,此时均通过pty设施。例如,假如某人在网上使用telnet程序连接到你的计算机上,则telnet程序即可能会打开/dev/ptmx设施获取一个fd。此时一个getty程序就应该运行在对应的/dev/pts/* 上。当telnet从远端获取了一个字符时,该字符就会通过ptmx、pts/* 传递给 getty程序,而getty程序就会通过pts/* 、ptmx和telnet程序往网络上返回“login:”字符串信息。这样,登录程序与telnet程序就通过“伪终端”进行通信。
telnet<—>/dev/ptmx(master)<—>pts/*(slave)<—>getty
假如一个程序把 pts/* 看作是一个串行端口设施,则它对该端口的读/写操作会反映在该逻辑终端设施对的另一个/dev/ptmx上,而/dev/ptmx则是另一个程序用于读写操作的逻辑设施。
这样,两个程序即可以通过这种逻辑设施进行互相交流,这很象是逻辑设施对之间的管道操作。对于pts/* ,任何设计成使用一个串行端口设施的程序都可以使用该逻辑设施。但对于使用/dev/ptmx的程序,则需要专门设计来使用/dev/ptmx逻辑设施。通过使用适当的软件,即可以把两个甚至多个伪终端设施连接到同一个物理串行端口上。
3、 串口终端(/dev/ttySn)
串行端口终端(Serial PortTerminal)是使用计算机串行端口连接的终端设施。计算机把每个串行端口都看作是一个字符设施。有段时间串行端口设施通常被称为终端设施,那时它的最大用途就是用来连接终端,所以这些串行端口所对应的设施名称是/dev/tts/0(或者/dev/ttyS0)、/dev/tts/1(或者/dev /ttyS1)等,设施号分别是(4,0)、(4,1)等(对应于win系统下的COM1、COM2等)。若要向一个端口发送数据,可以在命令行上把标准输出重定向到这些特殊文件名上就可。
我们可以在命令行提醒符下键入:echotekkaman> /dev/ttyS1会把“tekkaman”发送到连接在ttyS1(COM2)端口的设施上。
在2.6以后的内核后、少量三星的芯片将串口终端设施节点命名为ttySACn。TI的Omap系列芯片从2.6.37开始芯片自带的UART设施开始使用专有的的omap-uart驱动,故设施节点命名为ttyOn,以区别于使用8250驱动时的设施名“ttySn”。这其中包括笔者用到的这个S3C2440。所以我们在Uboot启动参数中要设置console = ttySAC0才可以。这一句的意思其实就是把ttySAC0当做我们的控制台终端。
4、 其它类型终端
还针对很多不同的字符设施存在有很多其它种类的终端设施特殊文件,例如针对ISDN设施的/dev/ttyIn终端设施等。
好了。到此为止。相信读者已经对tty设施有了一个概观。
由于我们和开发板的人机交互的接口是Windows下的串口控制台。这就是上面所说的控制台终端。但是我们用了console = ttySAC0.即把串口终端当做控制台终端。所以我们要研究具体的代码需要cd到serial子目录下。即串口终端目录。ls显示serial下的文件结点。如图所示:
image
我们主要关心的是两类文件。一类是与体系结构和板载资源无关的通用串口操作文件。(samsung.c)一类是与体系结构相关的硬件操作文件。(s3c2440.c s3c2410.c s5pv210.c等),我们为了得到具体的调用链。在具体的发送函数中加入回溯。如图所示。
image
我们得到的函数调用链是这样的(以发送函数。即文件的写操作为例.
write->
sys_write->
vfs_write->
redirected_tty_write->
tty_write->
n_tty_write->
uart_write->
uart_start->
s3c24xx_serial_start_tx
image
从具体代码上来看。这些函数基本上都是通过结构体中的函数指针调用。我们可以把这个调用链分为三个部分。即tty子系统核心。tty链路规程。tty驱动
tty核心。是对整个tty设施的笼统。对客户提供统一的接口。包括sys_write->vfs_write
tty线路规程。是对传输数据的格式化。在tty_ldisc_N_TTY变量中形容。包括redirected_tty_write->tty_write->n_tty_write->
tty驱动。是面向tty设施的硬件驱动。这里面真正的对硬件进行操作。包括uart_write->uart_start->s3c24xx_serial_start_tx
这是从具体函数的角度来看的调用链。下面为了从数据结构的角度来分析调用链。详情linux内核中针对于这一个串口硬件的主要数据结构。对于具体的字段我们用到的时候再解释。
uart_driver。
就是uart驱动程序结构。封装了tty_driver,使得底层的UART驱动无需关心tty_driver具体定义如下。
image
uart_port
uart_port用于形容一个UART端口(直接对应于一个串口)的I/O端口或者者IO内存地址等信息。
在这里插入图片形容
uart_ops定义了针对UART的一系列操作。注意这里不要把uart_ops结构和uart_ops变量混淆。uart_ops结构是我们这里的数据结构。而uart_ops变量则是一个tty_operations的变量。
在serial_core.c中定义了tty_operations的实例。即uart_ops变量,包含uart_open();uart_close();uart_send_xchar()等成员函数,这些函数借助uart_ops结构体中的成员函数来完成具体的操作:
image
uart_ops变量是tty_operations型的一个变量。如下图所示:
image
uart_state是uart的状态结构。
image
uart_info是uart的信息结构。在这个体系结构下定义为s3c24xx_uart_info:
image
所以很显然。用数据结构来形容函数调用链就是
uart_driver ->
uart_state->
uart_port->
uart_ops->
特定的函数指针。
初始化过程比较复杂。不赘述。从函数指针的调用流程为主线。忽略少量入参检查和内核中的信号量代码。大致的初始化流程如下图所示:
image
打开设施和初始化流程相似。如图所示:
image
同理数据的发送和接收如图所示:
image
这里我们需要注意的是。使能发送并没有真正的发送过程。而只是使能发送中断
这一句:enable_irq(ourport->tx_irq);
image
这是由于ARM9解决器上有一个循环缓冲。客户从write系统调用传下来的数据就会写入这个UTXH0寄存器。发送完事之后解决器会产生一个内部中断。我们通过这个内部中断即可以实现流控过程、我们打开芯片手册可以看到如下字样(拿ARM11举例也一样,。这是ARM11的):
image
如下才是发送中断的ISR(Interrupt Service Routine)中断服务例程。一个irqreturn_t类型的handler。
image
这个wr_regb(port, S3C2410_UTXH, port->x_char);就是往特定寄存器写的过程。
至此我们的分析已经结束。相信读者对于Linux下的tty子系统已经有一个概观了。下面是这个uart驱动的总图。结合数据结构的调用链。Linux内核完成了驱动模型和特定硬件的分离:
image
串口驱动数据结构总图:
image-20201102212833734
-END-
直接来源 | 嵌入式大杂烩
原文:https://www.jianshu.com/p/3a9013b9569c
作者:Linkerist
| *整理文章为传播相关技术,版权归原作者所有* |
*| 如有侵权,请联络删除* |
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 嵌入式驱动解析:从串口驱动到Linux驱动模型