VxWorks驱动开发笔记_第1页
VxWorks驱动开发笔记_第2页
VxWorks驱动开发笔记_第3页
VxWorks驱动开发笔记_第4页
VxWorks驱动开发笔记_第5页
已阅读5页,还剩295页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

精品文档 1欢迎下载 VxWorksVxWorks 驱动开发笔记驱动开发笔记 普通应用软件的开发 客户都会提出很明确的需求如功能 用户界面 外部接口以及开发周期经费等等要求 这 些要求一般都相对直观且容易理解 但是对于驱动程序的开发开说 开发周期以及经费这些需求往往都比较容易理解 可是对于功能 用户界面以及外部接口等需求就很难描述了 因为这需要对底层操作系统的理解 否则就无法提出适 宜的需求来 而对底层操作系统的理解才是驱动程序开发之所以困难的主要原因 1 11 1 驱动程序的结构驱动程序的结构 驱动程序有两大基本特征 一是它实现了对硬件设备的访问 最根本目的 二是它实现了一系列与硬件设备无关 的的访问接口 通过这些接口 上层软件在控制此类硬件设备时无需对硬件进行详细的了解就可以进行访问 此外 当硬件设备更换时 只需要修改设备驱动的硬件相关的部分 而上层软件无需做任何更改 这两个基本特征也正好决 定了驱动程序的主体结构 如图 1 1 所示 图中的阴影部分为设备驱动程序 图 1 1 驱动程序的结构 1 21 2 驱动程序的工作流程驱动程序的工作流程 不同设备在操作系统中完成的工作是不同的 但是就是工作流程来说 大致可以分为两个阶段 第一个阶段是初始化阶段 在初始化阶段 驱动程序主要完成硬件以及设备驱动相关数据结构的初始化 第二个阶段是硬件的访问阶段 根据设备工作模式的不同 可以分为中断模式和轮询模式 无论何种模式都可以 通过与硬件设备无关的通用接口进行硬件设备的访问 2 12 1 串口驱动原理串口驱动原理 串口因为调试简单在许多数据量不大的场合依然较为流行 可以借助串口对目标机中操作系统的运行情况进行监 控等等 下图为 Tornado 开发软件通过串口对目标机上运行的 VxWorks 操作系统进行监控的结构原理图 精品文档 2欢迎下载 图 2 1 Tornado 通过串口对 vxWorks 操作系统进行监控 设备的驱动程序分为与硬件相关部分和硬件无关部分 而硬件相关部分则负责具体的硬件实现 硬件无关部分实 现了一系列通用的数据接口 其中硬件无关部分实现是 create remove open close read write ioctl 等 7 个 通用的函数接口 使用这 7 个基本的函数 不但能够访问串口 而且还能够对网络 磁盘文件等多类设备进行访问 这 7 个函数的原型在文件 ioLib c 中进行定义 分别为 int creat const char name int flag 该函数创建了一个文件描述符 fd 其中 name 为一个抽象文件的文件 路径 这个抽象可以指硬盘上保存的一个文件 也可以是一个以字符串表示的一个设备 当文件描述符创建完毕后自 动以参数 flag 打开该文件 STATUS remove const char name 移除文件 name 表明文件路径 int open const char name int flags int mode 打开文件 准备访问 name 为文件路径 flags 为打开方式 如只读 只写 读写以及不存在可创建等 mode 为打开模式只有在 NFS 文件系统下才有效 打开成功后返回文件描述 符 STATUS close int fd 关闭文件 fd 为文件描述符 int read int fd char buffer size t maxbytes 从文件描述符 fd 指定的文件中读取最多 maxbytes 个字节 保存在 buffer 指定的位置 可能会由于文件长度的原因 实际读取的字节数小于 maxbytes 函数返回实际读出的字节 数 int write int fd char buffer size t nbytes 向文件描述符 fd 指定的文件中写入 nbytes 个字节的数据 原数据保存在 buffer 只能的内存中 可能会由于 buffer 空间等原因 实际写入的字节数小于 nbytes 函数返回实际 写入的字节数 int ioctl int fd int function int arg 控制函数 主要用于设置或读取设备的工作方式等特性 上述这 7 个通用接口 也就是串口驱动的一个最根本的需求 即通过这几个函数就可以实现对串口控制芯片 i8250 的 操作 而函数库 iosLib 中则是上述各个接口的较为底层的实现 它首先根据访问设备类型的不同 普通磁盘文件和硬 件设备 而将其分为两类 并用不同的数据结构描述 这里主要对结构 DRV ENTRY 进行分析 如图 2 3 精品文档 3欢迎下载 图 2 3 iosLib 库提供的数据结构结构 数组 drvTable 的每个元素 drvTable i 对应一类设备 因此其下表 i 将是不同类型设备之间区分的一个重要的参 考 由于 0 通常表示无效 因此 drvTable 0 为空 不代表任何设备 图 2 4 给出了 ioLib 提供的通用函数和结构 DRV ENTRY 的基本关系 图中的箭头表示调用关系 有调用者指向被调用者 图 2 4 ioLib 库与 iosLib 库的内部关系 由于数组 drvTable 的每个元素 drvTable i 对应一类设备 如果要使用某一类设备中的一个 如系统中两个串口 的一个 就必须指明是哪个串口 在函数库 ioLib 中的 7 个通用函数中都有一个重要的参数 name 表明文件的路径或 者设备的名称 该名称具有唯一性 也就是一个 name 指向唯一一个文件或者唯一一个设备 下面要引入的数据结构是 设备描述符 如图 2 5 所示 它在函数库 iosLib 中定义 精品文档 4欢迎下载 图 2 5 设备描述符 基本的设备描述符的数据结构为 DEV HDR 它描述了一些最为基本的信息 数组 decTable 的下标 devnum 和设备 名称 name 该设备序号即是 ioLib 库中使用的文件描述符 fd 综合起来设备描述符 DRV HDR 结构 DRV ENTRY 以及 7 个通用函数库的关系可以用图 2 6 来描述 图 2 6 DRV HDR DRV ENTRY 及通用接口之间的关系 上述通用接口可以满足网卡 串口等外围设备的需要 对于具体的外部设备 则需要对图 2 6 所说的通用接口进行扩 展 如图 2 7 所示 这个扩展主要采用了两种手段 一是继承 即通过对结构 DEV HDR 进行继承得到如串口 终端以 及网卡等特殊类型的设备 而是多态 也就是对一个通用的接口 根据具体设备的不同而采取不同的函数 C 不是面向 对象语言 没有明确的类 继承和多态的概念 但是可以灵活地使用 C 达到同样的目的 精品文档 5欢迎下载 图 2 7 通用接口通过多态和继承得到扩展 图 2 8 则更为详细地描述了 VxWorks 操作系统对多个设备的管理 对每类设备的操作函数 系统创建了一个 DRV ENTRY 结构数组来描述 每个 DRV ENTRY 结构变量对应一类设备的操作函数 对某类设备的具体操作方式的设置则 是通过设定 DRV ENTRY 结构变量中个函数指针来实现的 对于同一类的设备 系统为其从 DEV HDR 派生了一个数据结 构 而每个结构变量则是对应该类设备中的每一个具体的设备 不同的设备通过双向链表链接在一起 精品文档 6欢迎下载 图 2 8 VxWorks 对系统中多个设备的管理结构 2 32 3 串口驱动程序函数库分析串口驱动程序函数库分析 为了进一步加深对 IO 设备管理数据结构的理解 本节将分别对函数库 ioLib iosLib 进行分析 2 3 1 函数库 ioLib ioLib 库为上层提供了 7 个基本的函数接口 creat remove open close read write 以及 ioctl 上层用户只需要对这 7 个函数进行操作就能够完成对硬件的访问 下面一次分析 ioLib 库中各个函数库的功 能 1 int creat 精品文档 7欢迎下载 const char name int flag 该函数提供了一个与设备无关的通用接口 用于创建一个文件 可以是普通的磁盘文件也可以是抽象的设备文件 该文件的路径为 name 创建完毕后自动打开 打开的参数为 flag 该函数的正常返回值为文件描述符 否则将会返 回 ERROR 对照图 2 8 该函数根据设备的名称 从链表中找到该设备对应的设备号 drmNum 该数值即为函数返回的文件描 述符 然后以此为下标就可以找到该类设备的 de create 函数指针 从而找到该设备的 create 函数 2 int open const char name int flags int mode 该函数打开一个文件以方便进行读 写或者更新 打开后返回该文件的文件描述符 open 函数的参数为文件名 以及访问方式 l O RDONLY 0 以只读方式打开 l O WRONLY 1 以只写方式打开 l O RDWR 2 以读写方式打开 l O CREAT 0 x0200 如果文件不存在 就创建一个文件并打开 3 LOCAL int ioCreateOrOpen const char name int flags int mode BOOL create 这个函数其实是函数 create 和 open 函数的实现主体 create 和 open 函数只是根据参数 create 简单调用 函数 ioCreateOrOpen 而已 真正的实现在函数 ioCreateOrOpen 中 4 STATUS unlink char name 该函数主要是和 posix 兼容 它的功能与 remove 完全相同 5 STATUS remove const char name 调用函数 iosDelete 来删除文件 如果有符号链接 则需要沿着符号链接直接找到文件并删除 精品文档 8欢迎下载 6 STATUS close int fd 调用函数 ioClose 函数关闭文件 7 int rename const char oldname const char newname 修改文件名 并不是所有的设备都支持重命名操作 比如通常的 dosFS 和 rt11FS 都是支持重命名操作的 而 netDrv 和 nfsDrv 则不支持 因此在使用这个函数前需要明确设备是否支持重命名操作 重命名操作主要通过调用函数 ioctl fd FIORENAME int newname 完成 注意 调用函数 ioctl fd FIORENAME int newname 重命名前需要先打开文件 open 8 int read int fd char buffer size t maxbytes 调用函数 iosRead 实现读操作 maxbytes 为读取的最大字节数 读取之后存放在 buffer 指定的地址空间 不过 可能由于文件中字节数的限制等因素 实际读取的字节数可能会小于 maxbytes 因此需要在最终的返回值中返回实际 读取的字节数 9 int write int fd char buffer size t nbytes 调用函数 iosWrite 向指定的文件中写入数据 nbytes 为期望写入的字节数 但是实际上可能会由于文件本身的 限制导致实际写入的字节数小于 nbytes 因此需要在返回值中记录实际写入的字节数 10 int ioctl int fd int function int arg 直接调用函数 iosIoctl 实现对文件的控制操作 精品文档 9欢迎下载 11 int lseek int fd long offset int whence 设定一个文件的读写指针 下次读写操作将从设定的位置开始 参数 whence 有三个数值 l SEEK SET 0 设定到相对于文件起始位置偏移 offset 位置 l SEEK CUR 1 设定到当前位置偏移 offset 的位置 l SEEK END 2 相对于文件结束位置偏移 offset 的位置 注意 如果指定的地址是无效的地址 超出了文件的范围 那么将返回错误指示 12 int readv int fd struct iovec iov int iovcnt 从设备 fd 中读取数据 保存在 iov 起始地址为 iov 的数组中 iovcnt 为结构 iovec 数组 iov 的元素的个数 iov 是一个数组指针 它指向一个 iovec 结构数组 iovec 结构中的元素 iov base 指定了数据保存起始位置地址 而 iovcnt 则指定了 iov 数组的元素个数 因此总的读出的字节数为各个数组元素中保存数据的字节数之和 如图 2 9 所 示 图 2 9 iovec 结构示意图 13 int writev int fd register struct iovec iov int iovcnt 精品文档 10欢迎下载 该函数调用 iosWrite 函数将几段分散的数据写入到 fd 设备中 14 STATUS ioFullFileNameGet char pathName DEV HDR ppDevHdr char fullFileName 通常来说 一个完整的文件的路径包括两部分 该文件所在的设备路径 以及该文件在该设备上的相对路径 这 两部分组合得到了一个完整的路径 这个函数的作用就是根据参数 pathName 制定的路径名 这个路径名可以是相对 相对麽默认的路径 ioDefPath 路径名或者绝对路径名 函数 ioFullFileNameGet 的作用个就是根据提供的路径名找 到该文件坐在的设备及其相对于该设备的路径 其中设备名结构变量指针的指针保存在 DEV HDR ppDevHdr 参数中 返回 相对于设备的文件名则存放在参数 char fullFileName 中保存 举例来说当前默认路径为 dev sd1 x 相对路径名为 y txt 该函数将会找到设备 dev sd1 对应的 DEV HDR ppDevHdr 指针返回 并将 fullFileName 设置为 x y txt 返回 15 STATUS ioDefPathSet char name 设定默认路径 并将其复制到全局变量 ioDefPath 中 注意 name 必须是一个绝对路径 也就是说 name 指定的路 径必须包含一个有效的设备 即从 iosDevList 中能够搜索到的设备 16 void ioDefPathGet char pathname 将 ioDefPath 复制到 pathname 中返回 17 STATUS chdir char pathname 这个函数等价于函数 ioDefPathSet 可以认为是 ioDefPathSet 函数的一个别名 18 char getcwd char buffer int size ioDefPathGet 函数的别名 只是如果参数 size 指定的空间太小的话将返回 ERROR 19 char getwd 精品文档 11欢迎下载 char pathname ioDefPathGet 函数的别名 20 STATUS ioDefPathCat char name 修改默认的路径 如果 name 是一个包含设备路径的一个绝对路径名 那么它就成为了一个新的默认路径 否则 name 将会被连接到当前默认路径从而更新了新的默认路径 例如 如果默认路径为 sda x name sda y 则默认路径修改为 sda y 如果 name y 则默认路径修改 为 sda x y 21 void ioDefDevGet char devName 获取默认路径所属的设备的名称 22 void ioDefDirGet char dirName 这个函数在新的版本中将会删除 它的主要作用就是获取默认路径相对于所属设备的相对路径 如设备名为 sda 默认路径为 sda x 则 ioDefDevGet 函数获取了 sda 设备 而函数 ioDefDirGet 则获取 了相对路径 23 void ioGlobalStdSet int stdFd int newFd 文件 ioLib 中定义了一个数组 LOCAL int ioStdFd 3 该数组表明标准文件描述符 这三个标准的文件描述符 主要是 标准输入 0 标准输出 1 错误输出 2 这个函数的作用 就是设置这个数组中某个元素的文件描述符 stdFd 指的是 ioStdFd 数组中元素的下标 0 2 有效 newFd 数组中元素的数值 24 int ioGlobalStdGet int stdFd 获取数组 ioStdFd 中某个元素的数值 即文件描述符的数值 精品文档 12欢迎下载 25 void ioTaskStdSet int taskId int stdFd int newFd 对 VxWorks 系统来说 并不是每个任务的标准输入 标准输出 错误输出都是相同的 多数任务的标准输入是键 盘 标准输出是显示器等 但是对某些串口监控的任务来说 可以把它的标准输入设置为串口等等 每个任务描述符中都有一个数组 taskStd 表明这个任务的标准输入 标准输出以及错误输出 这个函数的功能 就是设置一个任务的标准输入 标准输出 错误输出 26 int ioTaskStdGet int taskId int stdFd 获取一个任务的标准输入 标准输出 错误输出 27 BOOL isatty int fd file descriptor to check 判断一个文件描述符是否是一个 tty 最初的计算机没有键盘和显示器 输入输出都比较麻烦 后来有 Teletype 公司生产了一种字符型终端设备 因此有很多计算机操作系统都把字符型终端设备叫做 tty 设备 关于文件路径的进一步详细操作可以参考函数库 pathLib 2 3 22 3 2 函数库函数库 iosLibiosLib 函数库 iosLib 是库 ioLib 的底层实现 它负责将各个硬件设备及其驱动组织起来 从而使得用户只需要调用 ioLib 库就可以通过类似于文件 IO 的访问方式来访问各个硬件设备 在对 iosLib 库函数进行分析的过程中我们将主要 分析系统是如何将这些设备以及驱动组织起来 以及如何为 ioLib 库提供接口的 在函数库 iosLib 中定义了一个通用的接口结构 DRV ENTRY 它为 ioLib 库提供了一个通用过的接口 使得 ioLib 无需了解具体的设备类型就可以对硬件设备进行访问 另一方面它又通过继承的方法 将不同类型的设备保存在链表 中进行统一管理 参见图 2 8 下面对设备的管理函数进行详细分析 1 static STATUS nullWrite int dummy char pBuf int nBytes 空函数 2 STATUS iosInit 精品文档 13欢迎下载 int max drivers int max files char nullDevName iosLib 函数库初始化 该函数的主要作用是动态创建了一个 FD ENTRY 结构数组和一个 DRV ENTRY 结构数组 并 进行了初始化 3 int iosDrvInstall FUNCPTR pCreate FUNCPTR pDelete FUNCPTR pOpen FUNCPTR pClose FUNCPTR pRead FUNCPTR pWrite FUNCPTR pIoctl 从 DRV ENTRY 结构数组中找到一个空闲的 DRV ENTRY 结构变量 将参数指定函数指针填充到该 注意由于最开始 的一个元素即 drvTable 0 中填充的是一个空设备 因此查找空闲的元素是从下表 1 开始查找的 如图 2 10 该函数 返回下标 i 图 2 10 函数 iosDrvInstall 的操作 正是这个步骤才使得一类设备的特殊访问方法与通用的接口连接起来 4 STATUS iosDrvRemove int drvnum BOOL forceClose 函数 iosDrvInstall 的逆过程 将 DRV ENTRY 结构数组中的下表为 drvnum 的元素清空 如果 forceClose 参数为 TRUE 则需要强制关闭该设备已经打开的文件 如果 forceClose 为 FALSE 但是该设备有打开的文件 则返回错误 精品文档 14欢迎下载 注意 一个设备可能对应多个打开的文件 5 STATUS iosDevAdd DEV HDR pDevHdr char name int drvnum 增加一个设备 该设备名称 路径 名为 name 该设备的 7 个访问函数在对应的 DRV ENTRY 结构数组中的下表为 drvnum 如图 2 11 所示 注意 在添加设备之前需要调用函数 iosDevMatch 检查已经添加的设备中是否包含相同的路径名 文件名 如果 有则说明该设备已经加入了 返回错误 此外就是该函数只修改了 iosDvList 链 并不触及 DRV ENTRY 结构数组的操 作 如果已经加入的设备中有 ab 这个名字 那么还可以增加名字为 abc a 这样的设备 但是名字为 ab 的再不能添加了 图 2 11 在设备管理库中加入新设备 6 void iosDevDelete DEV HDR pDevHdr pointer to device s structure 从 iosDvList 链中删除一个设备 注意添加的时候 DEV HDR 结构中的 name 是动态分配的 因此这里需要释放相 应的空间 这里可以参考 iosDevAdd 函数 在 iosDevAdd 函数中参数 pDevHdr 也不是动态分配的 7 DEV HDR iosDevFind 精品文档 15欢迎下载 char name name of the device char pNameTail where to put ptr to tail of name 根据名字查找一个设备的 DEV HDR 结构 查找遵照一下原则 l 调用函数 iosDevMatch 在 devTable 数组中查找与 name 最为匹配的 也就是说查找的设备名字与 name 的前面 n n 为设备名称的字符个数 个字符完全相同 l 如果查到了匹配的 则返回值 pNameTail 返回的则是 name 字符串中不包含设备名称的字符串 l 如果没有找到匹配的设备 则返回默认的设备 如果有的话 此时 pNameTail 返回的则是原 name 字符串 8 LOCAL DEV HDR iosDevMatch char name 该函数的作用是查找已经添加的设备名中与参数 name 匹配度最大设备名 所谓匹配度最大就是说设备名正好是 name 字符串的前 n n 为设备名称字符串长度 个字符完全相同中的所有设备中 n 最大的那一个设备 例如已经添加的 设备中有 ab abc bcde 那么与 name abcde 匹配最大的为 abc 9 DEV HDR iosNextDevGet DEV HDR pDev 从 node 链中查找下一个 node 的 DEV HDR 结构变量 参照前面的链表结构图 10 int iosCreate DEV HDR pDevHdr char fileName int mode 该函数根据 DEV HDR 结构指针 pDevHdr 中的 drvNum 数值作为下标 找到 drvTable 数组中的 DEV ENTRY 结构变量 中的 de create 函数指针 调用 de create 函数 11 int iosDelete DEV HDR pDevHdr char fileName 类似于函数 iosCreate 12 int iosOpen DEV HDR pDevHdr 精品文档 16欢迎下载 char fileName int flags int mode 类似于函数 iosCreate 13 STATUS iosClose int fd 类似于函数 iosCreate 14 int iosRead int fd char buffer int maxbytes 类似于函数 iosCreate 15 int iosWrite int fd char buffer int nbytes 类似于函数 iosCreate 16 int iosIoctl int fd int function int arg 基本的控制函数 主要完成几个功能 function FIOGETNAME 将文件描述符的名字复制到 arg 中 其他情况则执行 drvTable pFdEntry pDevHdr drvNum de ioctl 这个类似于函数 iosCreate 17 LOCAL void iosLock void 这个函数和 iosUnlock 共同管理一个二进制信号量 主要负责对 fdTable iosDevList 的访问保护 主要进行互斥操 作 精品文档 17欢迎下载 2 3 32 3 3 函数库函数库 ttyDrv cttyDrv c 前面主要分析了 vxWorks 系统 IO 设备管理的通用数据结构及其操作 这个通用接口既适用于串口类设备 又适 用于网络设备等等 对于串口这类设备来说 其驱动又有其独有的要求 如收发缓存的管理 波特率的设置等等 同 样 作为串口驱动的部分 也可以分为与硬件相关部分和与硬件无关部分 VxWorks 操作系统的串口驱动与硬件无关部分主要有两个函数库来实现 一是 ttyDrv 函数库 主要用于 7 个通用 函数中的 5 个函数的具体实现 另一个是 tyLib 函数库 主要用于处理收发缓冲区的处理 另一方面 从结构 TYCO DEV 来看 tyLib 的主要数据结构 TY DEV 变量包含在结构 TYCO DEV 因此其层次关系可以用图 2 12 所示 图 2 12 函数库 ttyDrv 与 tyLib 的层次关系 函数库 ttyDrv 主要通过结构 SIO CHAN 访问底层函数 在结构 TYCO DEV 中有一个 SIO CHAN 结构变量指针 该变 量指针指向底层的操作函数 如图 13 所示 图 2 13 ttyDrv 库与底层库的函数接口 首先分析 ttyDrv 函数库 这个函数库比较简单 直接分析各个函数 1 STATUS ttyDrv void 精品文档 18欢迎下载 函数 ttyDrv 主要调用了函数 iosDrvInstall 完成了 7 个函数的安装 对串口来说 系统并不要完全安装这 7 个 函数 比如 delete 函数 如果操作系统不要求 delete 串口驱动 那么这个函数也就不需要了 实际上 ttyDrv 函数主 要安装了基本操作函数 如图 2 14 图 2 14 函数 ttyDrv 主要安装的函数 这些函数中有 ttyOpen ttyClose ttyIoctl 是由函数库 ttyDrv C 提供的 其他函数如 tyRead tyWrite 则是 由函数库 tyLib 提供的 2 STATUS ttyDevCreate char name SIO CHAN pSioChan int rdBufSize int wrtBufSize 这个函数为一个 tty 设备创建了一个 TYCO DEV 数据结构并进行初始化 包括硬件的初始化 其中 pSioChain 是底层串行通道描述符 SIO CHAN 结构变量 的地址 rdBufSize 和 wrtBufSize 为读写缓冲区的大小 ttyDevCreate 函数在执行过程中调用了函数 tyDevInit 对 tyLib 库进行了初始化 注意调用函数 tyDevInit 的最 后一个参数 ttyStartup 这个函数对底层操作函数 pSioChan pDrvFuncs txStartup 进行了封装 也就是说只有函 数 ttyDrv 直接与底层硬件操作函数向关联 而 tyLib 库则完全被保护在了硬件无关层 参见图 2 12 ttyDevCreate 函数操作的数据结构如图 2 15 所示 同时它指定了串口的处理模式为中断模式 另一种为轮询 模式 精品文档 19欢迎下载 图 2 15 ttyDevCreate 函数操作的数据结构 注意这个函数和 ioLib 库中的 create 函数是完全不同的概念 该函数的调用应该在 create 之前 3 LOCAL int ttyOpen TYCO DEV pTyCoDev char name 精品文档 20欢迎下载 int flags int mode 打开一个 ttyDrv 串行设备 所谓打开设备就是使其处于准备收发数据的状态 对 i8250 串口控制芯片来说就是 设定计算机准备接收 DTR 和请求发送数据 RTS 的状态 注意 每调用一次函数 ttyOpen 该 pTyCoDev tyDev numOpen 就会自动加 1 表明该设备打开的次数 4 LOCAL int ttyClose TYCO DEV pTyCoDev 关闭一个 ttyDrv 设备 所谓关闭设备就是就是关闭其收发数据就绪的状态 注意 每调用一次函数 ttyClose pTyCoDev tyDev numOpen 就会自动减 1 表明该设备打开的次数 5 LOCAL int ttyIoctl TYCO DEV pTyCoDev int request void arg 对硬件设备的控制操作 request 是一个比较广义的操作 包含的种类繁多 而且对同样的操作 对不同的函数库来说 参数也可能会有 所不同 如波特率设置 在 ttyDrv 库 request 为 FIOBAUDRATE 这个参数在 iosLib h 中定义 而在 sioLib 库中该参 数为 SIO BAUD SET 因此需要进行参数的转换 另一方面 sioIoCtl 函数和 tyIoCtl 函数处理的职责是不同的 因此需 要首先尝试调用 sioIoCtl 函数 如果无此操作则尝试 tyIoCtl 函数 6 LOCAL void ttyStartup TYCO DEV pTyCoDev 发送数据函数 直接调用底层的发送函数 在函数 ttyDrv 中调用 iosDrvInstall 函数安装了两个函数 tyRead 和 tyWrite 这两个函数并在函数库 ttyDev 中 而是在 tyLib 库中 主要完成收发缓冲区的处理 下面要进行 tyLib 库的分析 2 3 42 3 4 函数库函数库 tyLibtyLib tyLib 函数在 ttyDrv 库与底层硬件操作之间建立了一个收发缓冲区 当上层函数需要接收数据时并不是直接读取 硬件的接收寄存器 而是调用函数 tyRead 读取 tyLib 的缓冲区 同样在发送数据时也是通过调用 tyWrite 函数将数据 发送给 tyLib 的缓冲区中 tyLib 负责对缓冲区进行管理 包括在轮询和中断模式下自动从串口控制器中读写数据 当接收缓冲区占用了一 定的空间之后 调用上层回调函数将接收缓冲区的数据取走等等 另外在不同的模式下 还要完成对接收缓冲区的数 据进行重新组织等任务 当前 tyLib 库的接收缓冲区管理主要实现了 line 模式和 raw 模式的缓冲区管理 所谓 line 模式就想当我们的中 断输入 只有在按下回车键以后 计算机才进行相应的动作 否则只是在屏幕上进行显示 如果在动作前按下了退格 精品文档 21欢迎下载 CTRL X CTRL C 等等按键 那么还要重新对新输入的 line 进行修改等等 这在利用串口连接终端的情况下是十分有用 的 在 raw 模式下 计算机每次指示读取相应的字符 并不对其中的含义进行解释 读取完毕后就根据缓冲区的情况 决定是否调用回调函数将接收缓冲区的数据取走 1 STATUS tyDevInit 下面分析 tyLib 库中的各个函数 FAST TY DEV ID pTyDev int rdBufSize int wrtBufSize FUNCPTR txStartup 用指定的参数初始化 tty 设备描述符 tty 设备描述符是一个数据结构 TY DEV 的变量而不是硬件设备 该结构变 量在函数调用前已经创建 因此这里只需要进行初始化就行了 而不用重新创建 注意区分互斥变量和二进制信号量的区别 互斥变量的目的是为了保证在一定的时间段内一个任务对某个资源独 占 即只有当前任务可以访问某一资源 在这期间其他任务无法获取到该资源 互斥变量的获取和释放都是由同一任 务完成 它主要强调资源的独占性 而二进制信号量则是某一任务为了获取某一资源 但是该资源却是由其他任务来 提供的 它的主要目的是为了确保不同任务的同步 即只有在资源有效的情况下 才能有任务实用该资源 二进制信 号量的获取和提供往往是由不同的任务来完成的 通常是任务 A 提供了某一资源 任务 B 等待到任务 A 提供了资源后 才能有效获取 二者的区别主要在于二进制信号量的资源是消耗性的 它分别由不同的任务生产和消耗 而互斥变量 保护的资源则是独占性 不存在生产和消耗之说 而仅仅是确保只有一个任务可以访问该资源 这里的二进制信号量 rdSyncSem 这个信号量确保底层串口驱动 tyIRd 在输入环状缓冲区保存了一定的数据之后 tyRead 上层函数才能有效获取该信号量并开始读取这些数据 二进制信号量 wrtSyncSem 则是确保在输出环状缓冲区中有足够的空间资源 当有了足够的空间资源则函数 tyITx 释放掉一定的空间时候信号量有效 而 tyWrite 只有在获取了该信号量后才可以向输出环状缓冲区中写入数据 互斥变量 mutexSem 的作用是为了确保对 TY DEV 结构变量的访问保护 2 STATUS tyDevRemove TY DEV ID pTyDev tyDevInit 函数的逆过程 注意这里 flushingRdBuf 和 flushingWrtBuf 要在 rdBuf 和 wrtBuf 内存被释放之前置 为 True 是为了确保中断处理程序 tyITx 和 tyIRd 不会在释放了之后访问这段内存 3 LOCAL void tyFlush FAST TY DEV ID pTyDev 这个函数比较简单 直接调用函数 tyFlushRd 和函数 tyFlushWrt 对各自的 buffer 进行 flush 精品文档 22欢迎下载 4 LOCAL void tyFlushRd FAST TY DEV ID pTyDev 该函数的作用是对输入环状缓冲区进行 flush 注意 flush 前需要将 flushingRdBuf 置位是为了确保不被中断处 理程序 tyIRd 搞乱 注意 flush 结束之后要发送一个 XON 信号提示对方串口设备接收准备好 并将变量 flushingRdBuf 复位为 False 5 LOCAL void tyFlushWrt FAST TY DEV ID pTyDev 该函数的作用是对输出环状缓冲区进行 flush 注意 flush 前需要将 flushingWrtBuf 置位是为了确保不被中断处 理程序 tyITx 搞乱 注意 flush 结束之后 free 空间很大 因此要给出一个 wrtSyncSem 信号量 并将变量 flushingWrtBuf 复位为 False 最后还要利用 func selWakeupAll 通知各个函数输出环状缓冲区已经空出来了 6 void tyAbortFuncSet FUNCPTR func 设置 abort 函数 当收到 abort 字符时执行 func 函数 默认为 null 7 void tyAbortSet char ch 设置 abort 字符 默认该字符为 CTRL C 8 void tyBackspaceSet char ch 设定 backspace 字符 默认字符为 CTRL H 9 void tyDeleteLineSet char ch 精品文档 23欢迎下载 设置行删除命令字符 默认为 CTRL U 10 void tyEOFSet char ch 设置 end of file 字符 默认该字符为 CTRL D 11 void tyMonitorTrapSet char ch 设置 TRAP TO MONITOR 字符 默认该字符为 CTRL X 当 OPT MON TRAP 选项有效时 输入 TRAP TO MONITOR 字符 将会终止通常的多任务系统并进入 TRAP TO MONITOR 程序 12 STATUS tyIoctl FAST TY DEV ID pTyDev int request int arg 处理 tty 设备的 ioctl 请求 现有的 bug 在 line 模式下 以参数 FIONREAD 调用返回的是输入缓冲区中的字符数 如果在 buffer 中有 5 个空 行 每个空行字符占用一字节 此外还有一个行字符数占用一字节 那么其返回值为 10 FIONREAD 返回接收环形缓冲区中的有效字符数 不包含正在输入的未完成的行 FIONWRITE 返回发送环形缓冲区中的有效字符数 FIOFLUSH flush 输入发送环形缓冲区 FIOWFLUSH flush 接收环形缓冲区 FIORFLUSH flush 发送环形缓冲区 FIOGETOPTIONS 读取设备选项 FIOSETOPTIONS 设置设备选项 FIOCANCEL 取消设备的读写 FIOISATTY 返回 TRUE FIOPROTOHOOK 设定函数 pTyDev protoHook FIOPROTOARG 设定函数 pTyDev protoHook 的参数 pTyDev protoArg FIORBUFSET 重新设定发送环形缓冲区 FIOWBUFSET 重新设定发送环形缓冲区 FIOSELECT 增加 selWakeupList FIOUNSELECT 删掉 selWakeupList 中指定的 item 精品文档 24欢迎下载 13 int tyWrite FAST TY DEV ID pTyDev char buffer FAST int nbytes 向 TY DEV 的发送环形缓冲区中写入数据 并启动 tyTxStartup 开始向 device 发送 注意在写入前 take 二进制信号量 wrtSyncSem 确保有足够的空间 写完后检查是否有空间 如果有空间则需要 give 二进制信号量 wrtSyncSem 方便其他任务写入 14 int tyRead FAST TY DEV ID pTyDev char buffer int maxbytes 从接收环形缓冲区中读取数据 pTyDev lnBytesLeft 变量记录了上次读取数据时 读取行中剩余的字节数 如 果为 0 说明已经完整地读取了一整行 否则说明上次读取的行中还有一定的字节没有读取 这时候可以直接读取 读 取完毕之后要检查接收环形缓冲区中空闲的空间否足够 如果足够的话且 OPT TANDEM 选项有效且当前为 XOFF 这时候 需要向 device 发送一个 XON 信号表明接收准备好 15 STATUS tyITx FAST TY DEV ID pTyDev char pChar 中断级处理程序 取出下一个要发送的字符 注意并不是所有要发送的字符都是要先从发送环形缓冲区中取出来 的 如 XON XOFF 字符 准备将该字符发送到串口控制芯片 并未发送 根据设定的规则 取出该字符需要根据如 下准则 l 首先检查接收端口是否需要发送 XON XOFF 信号 需要发送的话 rdState pending 为 True 如果需要发送 则下一个发送的字符为 XON XOFF l 如果 wrtState xoff 为 true 说明发送处于关闭状态 此时是不能够发送数据的 因此直接退出并置 pTyDev wrtState busy 为 FALSE 否则如果 wrtState flushingWrtBuf 为 true 则说明正在清空发送 buffer 此时直 接置 pTyDev wrtState busy 为 FALSE l 如果 wrtState cr 为 true 说明下一个要发送的字符为 n l 检查发送环形缓冲区中是否有要发送的字符 这一步要进行判断 如果取出的是一个 n 字符且在 OPT CRMOD 模式下 说明该字符要分拆成两个字符连续发送 首先发送 r 并置 wrtState cr 为 TRUE 说明下次会发送字符 n 发送完毕后要检查发送环形缓冲区是否有足够的空余空间 如果有的话要调用函数 func selWakeupAll 通 知相应的程序 可以向 buffer 中写入数据了 精品文档 25欢迎下载 注意 wrtState xoff rdState xoff rdState pending 以及 wrtState busy 三个变量的区别如下 l wrtState xoff 表明对方是否准备好接收 如果准备好则向本机发送 XON 未准备好发送 XOFF 收到的 XON XOFF 数值记入 wrtState xoff l rdState xoff 表明接收环形缓冲区的状态 true 表明 buffer 已经没有足够的空间来接收新的数据了 flase 则表明还有足够的空间来接收新数据 只有 rdState pending 状态发生了变化时才向对方发送状态数据 XON XOFF 信号 l 而 rdState pending 为 true 表明 rdState xoff 的状态刚刚发生了变化 此时需要在发送端口发送 rdState xoff 的状态 l wrtState busy 则是说明有程序正在将发送环形缓冲区的字符写到串口的发送端发送 16 STATUS tyIRd FAST TY DEV ID pTyDev FAST char inchar 中断处理程序调用 主要接收串口芯片接收数据寄存器的数据 并根据情况进行处理或者将字符转移到接收环形 缓冲区中 接收到字符后有一些细节的处理 这里不做太多的描述 只需要了解 接收完毕后要检查 buffer 中的 free 空间 是否小于门限值 以及当前行是否输入结束 如果输入结束 需要调用函数 func selWakeupAll 准备接受数据 17 LOCAL void tyRdXoff FAST TY DEV ID pTyDev pointer to device structure FAST BOOL xoff 该函数有两个作用 一是根据参数修改 rdState xoff 的数值 二是通过调用底层函数 pTyDev txStartup 准备 向串口控制器中发送 XON XOFF 信号 注意 当接收环形缓冲区没有足够空间的时候或者腾出了足够空间时调用 而且 仅在 rdState xoff 发生变化的时候才会发送一次 发送前需要将 rdState pending 置为 true 18 LOCAL void tyWrtXoff FAST TY DEV ID pTyDev BOO

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论