




已阅读5页,还剩290页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
学习资料收集于网络,仅供参考VxWorks驱动开发笔记普通应用软件的开发,客户都会提出很明确的需求如功能、用户界面、外部接口以及开发周期经费等等要求,这些要求一般都相对直观且容易理解。但是对于驱动程序的开发开说,开发周期以及经费这些需求往往都比较容易理解,可是对于功能、用户界面以及外部接口等需求就很难描述了,因为这需要对底层操作系统的理解,否则就无法提出适宜的需求来,而对底层操作系统的理解才是驱动程序开发之所以困难的主要原因。1.1驱动程序的结构驱动程序有两大基本特征:一是它实现了对硬件设备的访问(最根本目的),二是它实现了一系列与硬件设备无关的的访问接口。通过这些接口,上层软件在控制此类硬件设备时无需对硬件进行详细的了解就可以进行访问,此外,当硬件设备更换时,只需要修改设备驱动的硬件相关的部分,而上层软件无需做任何更改。这两个基本特征也正好决定了驱动程序的主体结构。如图1.1所示,图中的阴影部分为设备驱动程序。图1.1驱动程序的结构1.2驱动程序的工作流程不同设备在操作系统中完成的工作是不同的,但是就是工作流程来说,大致可以分为两个阶段。第一个阶段是初始化阶段,在初始化阶段,驱动程序主要完成硬件以及设备驱动相关数据结构的初始化。第二个阶段是硬件的访问阶段,根据设备工作模式的不同,可以分为中断模式和轮询模式,无论何种模式都可以通过与硬件设备无关的通用接口进行硬件设备的访问。2.1串口驱动原理串口因为调试简单在许多数据量不大的场合依然较为流行,可以借助串口对目标机中操作系统的运行情况进行监控等等。下图为Tornado开发软件通过串口对目标机上运行的VxWorks操作系统进行监控的结构原理图。图2.1Tornado通过串口对vxWorks操作系统进行监控设备的驱动程序分为与硬件相关部分和硬件无关部分,而硬件相关部分则负责具体的硬件实现,硬件无关部分实现了一系列通用的数据接口,其中硬件无关部分实现是create、remove、open、close、read、write、ioctl等7个通用的函数接口。使用这7个基本的函数,不但能够访问串口,而且还能够对网络、磁盘文件等多类设备进行访问。这7个函数的原型在文件ioLib.c中进行定义。分别为:intcreat(constchar*name,intflag):该函数创建了一个文件描述符fd。其中name为一个抽象文件的文件路径,这个抽象可以指硬盘上保存的一个文件,也可以是一个以字符串表示的一个设备,当文件描述符创建完毕后自动以参数flag打开该文件。STATUSremove(constchar*name):移除文件,name表明文件路径。intopen(constchar*name,intflags,intmode);打开文件,准备访问。name为文件路径,flags为打开方式如只读、只写、读写以及不存在可创建等,mode为打开模式只有在NFS文件系统下才有效。打开成功后返回文件描述符。STATUSclose(intfd);关闭文件。fd为文件描述符。intread(intfd,char*buffer,size_tmaxbytes);从文件描述符fd指定的文件中读取最多maxbytes个字节,保存在buffer指定的位置。可能会由于文件长度的原因,实际读取的字节数小于maxbytes,函数返回实际读出的字节数。intwrite(intfd,char*buffer,size_tnbytes);向文件描述符fd指定的文件中写入nbytes个字节的数据,原数据保存在buffer只能的内存中。可能会由于buffer空间等原因,实际写入的字节数小于nbytes,函数返回实际写入的字节数。intioctl(intfd,intfunction,intarg);控制函数,主要用于设置或读取设备的工作方式等特性。上述这7个通用接口,也就是串口驱动的一个最根本的需求,即通过这几个函数就可以实现对串口控制芯片i8250的操作。而函数库iosLib中则是上述各个接口的较为底层的实现。它首先根据访问设备类型的不同(普通磁盘文件和硬件设备)而将其分为两类,并用不同的数据结构描述,这里主要对结构DRV_ENTRY进行分析。如图2.3。图2.3iosLib库提供的数据结构结构数组drvTable的每个元素drvTablei对应一类设备,因此其下表i将是不同类型设备之间区分的一个重要的参考。由于0通常表示无效,因此drvTable0为空,不代表任何设备。图2.4给出了ioLib提供的通用函数和结构DRV_ENTRY的基本关系。图中的箭头表示调用关系,有调用者指向被调用者。图2.4ioLib库与iosLib库的内部关系由于数组drvTable的每个元素drvTablei对应一类设备,如果要使用某一类设备中的一个,如系统中两个串口的一个,就必须指明是哪个串口。在函数库ioLib中的7个通用函数中都有一个重要的参数name,表明文件的路径或者设备的名称,该名称具有唯一性,也就是一个name指向唯一一个文件或者唯一一个设备。下面要引入的数据结构是设备描述符,如图2.5所示,它在函数库iosLib中定义。图2.5设备描述符基本的设备描述符的数据结构为DEV_HDR,它描述了一些最为基本的信息,数组decTable的下标devnum和设备名称name,该设备序号即是ioLib库中使用的文件描述符fd。综合起来设备描述符DRV_HDR、结构DRV_ENTRY以及7个通用函数库的关系可以用图2.6来描述。图2.6DRV_HDR、DRV_ENTRY及通用接口之间的关系上述通用接口可以满足网卡、串口等外围设备的需要,对于具体的外部设备,则需要对图2.6所说的通用接口进行扩展,如图2.7所示,这个扩展主要采用了两种手段,一是继承,即通过对结构DEV_HDR进行继承得到如串口、终端以及网卡等特殊类型的设备;而是多态,也就是对一个通用的接口,根据具体设备的不同而采取不同的函数。C不是面向对象语言,没有明确的类、继承和多态的概念,但是可以灵活地使用C达到同样的目的。图2.7通用接口通过多态和继承得到扩展图2.8则更为详细地描述了VxWorks操作系统对多个设备的管理。对每类设备的操作函数,系统创建了一个DRV_ENTRY结构数组来描述,每个DRV_ENTRY结构变量对应一类设备的操作函数,对某类设备的具体操作方式的设置则是通过设定DRV_ENTRY结构变量中个函数指针来实现的;对于同一类的设备,系统为其从DEV_HDR派生了一个数据结构,而每个结构变量则是对应该类设备中的每一个具体的设备,不同的设备通过双向链表链接在一起。图2.8VxWorks对系统中多个设备的管理结构2.3串口驱动程序函数库分析为了进一步加深对IO设备管理数据结构的理解,本节将分别对函数库ioLib、iosLib进行分析。2.3.1函数库ioLibioLib库为上层提供了7个基本的函数接口:creat(),remove(),open(),close(),read(),write()以及ioctl()。上层用户只需要对这7个函数进行操作就能够完成对硬件的访问。下面一次分析ioLib库中各个函数库的功能。1.intcreat(constchar*name,intflag)该函数提供了一个与设备无关的通用接口,用于创建一个文件(可以是普通的磁盘文件也可以是抽象的设备文件),该文件的路径为name,创建完毕后自动打开,打开的参数为flag。该函数的正常返回值为文件描述符,否则将会返回ERROR。对照图2.8,该函数根据设备的名称,从链表中找到该设备对应的设备号drmNum(该数值即为函数返回的文件描述符),然后以此为下标就可以找到该类设备的de_create函数指针,从而找到该设备的create函数。2.intopen(constchar*name,intflags,intmode)该函数打开一个文件以方便进行读、写或者更新,打开后返回该文件的文件描述符。open()函数的参数为文件名以及访问方式:lO_RDONLY(0)以只读方式打开lO_WRONLY(1)以只写方式打开lO_RDWR(2)以读写方式打开lO_CREAT(0x0200)如果文件不存在,就创建一个文件并打开。3.LOCALintioCreateOrOpen(constchar*name,intflags,intmode,BOOLcreate)这个函数其实是函数create()和open()函数的实现主体。create()和open()函数只是根据参数create简单调用函数ioCreateOrOpen()而已,真正的实现在函数ioCreateOrOpen()中。4.STATUSunlink(char*name)该函数主要是和posix兼容,它的功能与remove完全相同。5.STATUSremove(constchar*name)调用函数iosDelete来删除文件。如果有符号链接,则需要沿着符号链接直接找到文件并删除。6.STATUSclose(intfd)调用函数ioClose函数关闭文件。7.intrename(constchar*oldname,constchar*newname)修改文件名。并不是所有的设备都支持重命名操作,比如通常的dosFS和rt11FS都是支持重命名操作的,而netDrv和nfsDrv则不支持,因此在使用这个函数前需要明确设备是否支持重命名操作。重命名操作主要通过调用函数ioctl(fd,FIORENAME,(int)newname)完成。注意:调用函数ioctl(fd,FIORENAME,(int)newname)重命名前需要先打开文件open()。8.intread(intfd,char*buffer,size_tmaxbytes)调用函数iosRead实现读操作,maxbytes为读取的最大字节数,读取之后存放在buffer指定的地址空间,不过可能由于文件中字节数的限制等因素,实际读取的字节数可能会小于maxbytes,因此需要在最终的返回值中返回实际读取的字节数。9.intwrite(intfd,char*buffer,size_tnbytes)调用函数iosWrite向指定的文件中写入数据,nbytes为期望写入的字节数,但是实际上可能会由于文件本身的限制导致实际写入的字节数小于nbytes,因此需要在返回值中记录实际写入的字节数。10.intioctl(intfd,intfunction,intarg)直接调用函数iosIoctl实现对文件的控制操作。11.intlseek(intfd,longoffset,intwhence)设定一个文件的读写指针,下次读写操作将从设定的位置开始。参数whence有三个数值:lSEEK_SET(0)设定到相对于文件起始位置偏移offset位置。lSEEK_CUR(1)设定到当前位置偏移offset的位置。lSEEK_END(2)相对于文件结束位置偏移offset的位置。注意:如果指定的地址是无效的地址(超出了文件的范围),那么将返回错误指示。12.intreadv(intfd,structiovec*iov,intiovcnt)从设备fd中读取数据,保存在iov起始地址为iov的数组中。iovcnt为结构iovec数组iov的元素的个数。iov是一个数组指针,它指向一个iovec结构数组,iovec结构中的元素iov_base指定了数据保存起始位置地址,而iovcnt则指定了iov数组的元素个数,因此总的读出的字节数为各个数组元素中保存数据的字节数之和。如图2.9所示。图2.9iovec结构示意图13.intwritev(intfd,registerstructiovec*iov,intiovcnt)该函数调用iosWrite函数将几段分散的数据写入到fd设备中。14.STATUSioFullFileNameGet(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.STATUSioDefPathSet(char*name)设定默认路径,并将其复制到全局变量ioDefPath中,注意name必须是一个绝对路径,也就是说name指定的路径必须包含一个有效的设备(即从iosDevList中能够搜索到的设备)。16.voidioDefPathGet(char*pathname)将ioDefPath复制到pathname中返回。17.STATUSchdir(char*pathname)这个函数等价于函数ioDefPathSet(),可以认为是ioDefPathSet()函数的一个别名。18.char*getcwd(char*buffer,intsize)ioDefPathGet()函数的别名。只是如果参数size指定的空间太小的话将返回ERROR。19.char*getwd(char*pathname)ioDefPathGet()函数的别名。20.STATUSioDefPathCat(char*name)修改默认的路径。如果name是一个包含设备路径的一个绝对路径名,那么它就成为了一个新的默认路径。否则name将会被连接到当前默认路径从而更新了新的默认路径。例如:如果默认路径为sda/x,name=sda/y,则默认路径修改为sda/y;如果name=/y,则默认路径修改为sda/x/y.21.voidioDefDevGet(char*devName)获取默认路径所属的设备的名称。22.voidioDefDirGet(char*dirName)这个函数在新的版本中将会删除。它的主要作用就是获取默认路径相对于所属设备的相对路径。如设备名为sda,默认路径为sda/x,则ioDefDevGet()函数获取了sda设备,而函数ioDefDirGet()则获取了相对路径。23.voidioGlobalStdSet(intstdFd,intnewFd)文件ioLib中定义了一个数组LOCALintioStdFd3,该数组表明标准文件描述符。这三个标准的文件描述符主要是:标准输入0、标准输出1、错误输出2。这个函数的作用,就是设置这个数组中某个元素的文件描述符。stdFd:指的是ioStdFd数组中元素的下标,02有效。newFd:数组中元素的数值。24.intioGlobalStdGet(intstdFd)获取数组ioStdFd中某个元素的数值,即文件描述符的数值。25.voidioTaskStdSet(inttaskId,intstdFd,intnewFd)对VxWorks系统来说,并不是每个任务的标准输入、标准输出、错误输出都是相同的,多数任务的标准输入是键盘,标准输出是显示器等,但是对某些串口监控的任务来说,可以把它的标准输入设置为串口等等。每个任务描述符中都有一个数组taskStd,表明这个任务的标准输入、标准输出以及错误输出,这个函数的功能,就是设置一个任务的标准输入、标准输出、错误输出。26.intioTaskStdGet(inttaskId,intstdFd)获取一个任务的标准输入、标准输出、错误输出27.BOOLisatty(intfd/*filedescriptortocheck*/)判断一个文件描述符是否是一个tty。最初的计算机没有键盘和显示器,输入输出都比较麻烦,后来有Teletype公司生产了一种字符型终端设备,因此有很多计算机操作系统都把字符型终端设备叫做tty设备。关于文件路径的进一步详细操作可以参考函数库pathLib。2.3.2函数库iosLib函数库iosLib是库ioLib的底层实现,它负责将各个硬件设备及其驱动组织起来,从而使得用户只需要调用ioLib库就可以通过类似于文件IO的访问方式来访问各个硬件设备。在对iosLib库函数进行分析的过程中我们将主要分析系统是如何将这些设备以及驱动组织起来,以及如何为ioLib库提供接口的。在函数库iosLib中定义了一个通用的接口结构DRV_ENTRY,它为ioLib库提供了一个通用过的接口,使得ioLib无需了解具体的设备类型就可以对硬件设备进行访问;另一方面它又通过继承的方法,将不同类型的设备保存在链表中进行统一管理(参见图2.8)。下面对设备的管理函数进行详细分析。1.staticSTATUSnullWrite(intdummy,char*pBuf,intnBytes)空函数。2.STATUSiosInit(intmax_drivers,intmax_files,char*nullDevName)iosLib函数库初始化,该函数的主要作用是动态创建了一个FD_ENTRY结构数组和一个DRV_ENTRY结构数组,并进行了初始化。3.intiosDrvInstall(FUNCPTRpCreate,FUNCPTRpDelete,FUNCPTRpOpen,FUNCPTRpClose,FUNCPTRpRead,FUNCPTRpWrite,FUNCPTRpIoctl)从DRV_ENTRY结构数组中找到一个空闲的DRV_ENTRY结构变量,将参数指定函数指针填充到该。注意由于最开始的一个元素即drvTable0中填充的是一个空设备,因此查找空闲的元素是从下表1开始查找的。如图2.10。该函数返回下标i。图2.10函数iosDrvInstall的操作正是这个步骤才使得一类设备的特殊访问方法与通用的接口连接起来。4.STATUSiosDrvRemove(intdrvnum,BOOLforceClose)函数iosDrvInstall的逆过程,将DRV_ENTRY结构数组中的下表为drvnum的元素清空。如果forceClose参数为TRUE则需要强制关闭该设备已经打开的文件。如果forceClose为FALSE但是该设备有打开的文件,则返回错误。注意:一个设备可能对应多个打开的文件。5.STATUSiosDevAdd(DEV_HDR*pDevHdr,char*name,intdrvnum)增加一个设备,该设备名称(路径)名为name,该设备的7个访问函数在对应的DRV_ENTRY结构数组中的下表为drvnum。如图2.11所示。注意,在添加设备之前需要调用函数iosDevMatch检查已经添加的设备中是否包含相同的路径名(文件名)如果有则说明该设备已经加入了。返回错误。此外就是该函数只修改了iosDvList链,并不触及DRV_ENTRY结构数组的操作。如果已经加入的设备中有“ab”这个名字,那么还可以增加名字为“abc”、“a”这样的设备,但是名字为“ab”的再不能添加了。图2.11在设备管理库中加入新设备6.voidiosDevDelete(DEV_HDR*pDevHdr/*pointertodevicesstructure*/)从iosDvList链中删除一个设备。注意添加的时候DEV_HDR结构中的name是动态分配的,因此这里需要释放相应的空间。这里可以参考iosDevAdd函数,在iosDevAdd函数中参数pDevHdr也不是动态分配的。7.DEV_HDR*iosDevFind(char*name,/*nameofthedevice*/char*pNameTail/*wheretoputptrtotailofname*/)根据名字查找一个设备的DEV_HDR结构。查找遵照一下原则:l调用函数iosDevMatch在devTable数组中查找与name最为匹配的,也就是说查找的设备名字与name的前面n(n为设备名称的字符个数)个字符完全相同;l如果查到了匹配的,则返回值*pNameTail返回的则是name字符串中不包含设备名称的字符串;l如果没有找到匹配的设备,则返回默认的设备(如果有的话),此时*pNameTail返回的则是原name字符串。8.LOCALDEV_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.intiosCreate(DEV_HDR*pDevHdr,char*fileName,intmode)该函数根据DEV_HDR结构指针pDevHdr中的drvNum数值作为下标,找到drvTable数组中的DEV_ENTRY结构变量中的de_create函数指针,调用de_create函数。11.intiosDelete(DEV_HDR*pDevHdr,char*fileName)类似于函数iosCreate()。12.intiosOpen(DEV_HDR*pDevHdr,char*fileName,intflags,intmode)类似于函数iosCreate()。13.STATUSiosClose(intfd)类似于函数iosCreate()。14.intiosRead(intfd,char*buffer,intmaxbytes)类似于函数iosCreate()。15.intiosWrite(intfd,char*buffer,intnbytes)类似于函数iosCreate()。16.intiosIoctl(intfd,intfunction,intarg)基本的控制函数,主要完成几个功能。function=FIOGETNAME,将文件描述符的名字复制到arg中;其他情况则执行drvTablepFdEntry-pDevHdr-drvNum.de_ioctl,这个类似于函数iosCreate()。17.LOCALvoidiosLock(void)这个函数和iosUnlock共同管理一个二进制信号量,主要负责对fdTable、iosDevList的访问保护。主要进行互斥操作。2.3.3函数库ttyDrv.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.13ttyDrv库与底层库的函数接口首先分析ttyDrv函数库,这个函数库比较简单,直接分析各个函数。1.STATUSttyDrv(void)函数ttyDrv主要调用了函数iosDrvInstall完成了7个函数的安装,对串口来说,系统并不要完全安装这7个函数,比如delete函数,如果操作系统不要求delete串口驱动,那么这个函数也就不需要了。实际上ttyDrv函数主要安装了基本操作函数,如图2.14。图2.14函数ttyDrv主要安装的函数这些函数中有ttyOpen、ttyClose、ttyIoctl是由函数库ttyDrv.C提供的,其他函数如tyRead、tyWrite则是由函数库tyLib提供的。2.STATUSttyDevCreate(char*name,SIO_CHAN* pSioChan,intrdBufSize,intwrtBufSize)这个函数为一个tty设备创建了一个TYCO_DEV数据结构并进行初始化(包括硬件的初始化)。其中pSioChain是底层串行通道描述符(SIO_CHAN结构变量)的地址。rdBufSize和wrtBufSize为读写缓冲区的大小。ttyDevCreate函数在执行过程中调用了函数tyDevInit对tyLib库进行了初始化。注意调用函数tyDevInit的最后一个参数ttyStartup,这个函数对底层操作函数(pSioChan)-pDrvFuncs-txStartup进行了封装,也就是说只有函数ttyDrv直接与底层硬件操作函数向关联,而tyLib库则完全被保护在了硬件无关层(参见图2.12)。ttyDevCreate()函数操作的数据结构如图2.15所示。同时它指定了串口的处理模式为中断模式(另一种为轮询模式)。图2.15ttyDevCreate()函数操作的数据结构注意这个函数和ioLib库中的create函数是完全不同的概念,该函数的调用应该在create之前。3.LOCALintttyOpen(TYCO_DEV* pTyCoDev,char* name,int flags,int mode)打开一个ttyDrv串行设备。所谓打开设备就是使其处于准备收发数据的状态,对i8250串口控制芯片来说就是设定计算机准备接收(DTR)和请求发送数据(RTS)的状态。注意:每调用一次函数ttyOpen,该pTyCoDev-tyDev.numOpen就会自动加1,表明该设备打开的次数。4.LOCALintttyClose(TYCO_DEV* pTyCoDev)关闭一个ttyDrv设备。所谓关闭设备就是就是关闭其收发数据就绪的状态。注意:每调用一次函数ttyClose,pTyCoDev-tyDev.numOpen就会自动减1,表明该设备打开的次数。5.LOCALintttyIoctl(TYCO_DEV* pTyCoDev,int request,void* arg)对硬件设备的控制操作。request是一个比较广义的操作,包含的种类繁多,而且对同样的操作,对不同的函数库来说,参数也可能会有所不同,如波特率设置,在ttyDrv库request为FIOBAUDRATE,这个参数在iosLib.h中定义,而在sioLib库中该参数为SIO_BAUD_SET,因此需要进行参数的转换;另一方面sioIoCtl函数和tyIoCtl函数处理的职责是不同的,因此需要首先尝试调用sioIoCtl函数,如果无此操作则尝试tyIoCtl函数。6.LOCALvoidttyStartup(TYCO_DEV*pTyCoDev)发送数据函数。直接调用底层的发送函数。在函数ttyDrv中调用iosDrvInstall函数安装了两个函数tyRead和tyWrite,这两个函数并在函数库ttyDev中,而是在tyLib库中。主要完成收发缓冲区的处理,下面要进行tyLib库的分析。2.3.4函数库tyLibtyLib函数在ttyDrv库与底层硬件操作之间建立了一个收发缓冲区,当上层函数需要接收数据时并不是直接读取硬件的接收寄存器,而是调用函数tyRead读取tyLib的缓冲区,同样在发送数据时也是通过调用tyWrite函数将数据发送给tyLib的缓冲区中。tyLib负责对缓冲区进行管理,包括在轮询和中断模式下自动从串口控制器中读写数据,当接收缓冲区占用了一定的空间之后,调用上层回调函数将接收缓冲区的数据取走等等,另外在不同的模式下,还要完成对接收缓冲区的数据进行重新组织等任务。当前tyLib库的接收缓冲区管理主要实现了line模式和raw模式的缓冲区管理。所谓line模式就想当我们的中断输入,只有在按下回车键以后,计算机才进行相应的动作,否则只是在屏幕上进行显示,如果在动作前按下了退格、CTRL-X、CTRL-C等等按键,那么还要重新对新输入的line进行修改等等。这在利用串口连接终端的情况下是十分有用的。在raw模式下,计算机每次指示读取相应的字符,并不对其中的含义进行解释,读取完毕后就根据缓冲区的情况决定是否调用回调函数将接收缓冲区的数据取走。1.STATUStyDevInit下面分析tyLib库中的各个函数。(FASTTY_DEV_IDpTyDev,intrdBufSize,intwrtBufSize,FUNCPTRtxStartup)用指定的参数初始化tty设备描述符。tty设备描述符是一个数据结构TY_DEV的变量而不是硬件设备,该结构变量在函数调用前已经创建,因此这里只需要进行初始化就行了,而不用重新创建。注意区分互斥变量和二进制信号量的区别。互斥变量的目的是为了保证在一定的时间段内一个任务对某个资源独占,即只有当前任务可以访问某一资源,在这期间其他任务无法获取到该资源,互斥变量的获取和释放都是由同一任务完成,它主要强调资源的独占性;而二进制信号量则是某一任务为了获取某一资源,但是该资源却是由其他任务来提供的,它的主要目的是为了确保不同任务的同步,即只有在资源有效的情况下,才能有任务实用该资源,二进制信号量的获取和提供往往是由不同的任务来完成的,通常是任务A提供了某一资源,任务B等待到任务A提供了资源后才能有效获取。二者的区别主要在于二进制信号量的资源是消耗性的,它分别由不同的任务生产和消耗;而互斥变量保护的资源则是独占性,不存在生产和消耗之说,而仅仅是确保只有一个任务可以访问该资源。这里的二进制信号量rdSyncSem,这个信号量确保底层串口驱动tyIRd在输入环状缓冲区保存了一定的数据之后,tyRead上层函数才能有效获取该信号量并开始读取这些数据。二进制信号量wrtSyncSem则是确保在输出环状缓冲区中有足够的空间资源,当有了足够的空间资源则函数tyITx释放掉一定的空间时候信号量有效,而tyWrite只有在获取了该信号量后才可以向输出环状缓冲区中写入数据。互斥变量mutexSem的作用是为了确保对TY_DEV结构变量的访问保护。2.STATUStyDevRemove(TY_DEV_IDpTyDev)tyDevInit函数的逆过程。注意这里flushingRdBuf和flushingWrtBuf要在rdBuf和wrtBuf内存被释放之前置为True是为了确保中断处理程序tyITx和tyIRd不会在释放了之后访问这段内存。3.LOCALvoidtyFlush(FASTTY_DEV_IDpTyDev)这个函数比较简单,直接调用函数tyFlushRd和函数tyFlushWrt对各自的buffer进行flush。4.LOCALvoidtyFlushRd(FASTTY_DEV_IDpTyDev)该函数的作用是对输入环状缓冲区进行flush。注意flush前需要将flushingRdBuf置位是为了确保不被中断处理程序tyIRd搞乱。注意flush结束之后要发送一个XON信号提示对方串口设备接收准备好,并将变量flushingRdBuf复位为False。5.LOCALvoidtyFlushWrt(FASTTY_DEV_IDpTyDev)该函数的作用是对输出环状缓冲区进行flush。注意flush前需要将flushingWrtBuf置位是为了确保不被中断处理程序tyITx搞乱。注意flush结束之后free空间很大,因此要给出一个wrtSyncSem信号量,并将变量flushingWrtBuf复位为False。最后还要利用_func_selWakeupAll通知各个函数输出环状缓冲区已经空出来了。6.voidtyAbortFuncSet(FUNCPTRfunc)设置abort函数,当收到abort字符时执行func函数。默认为null。7.voidtyAbortSet(charch)设置abort字符,默认该字符为CTRL-C。8.voidtyBackspaceSet(charch)设定backspace字符,默认字符为CTRL-H。9.voidtyDeleteLineSet(charch)设置行删除命令字符,默认为CTRL-U。10.voidtyEOFSet(charch)设置end-of-file字符,默认该字符为CTRL-D。11.voidtyMonitorTrapSet(charch)设置TRAP-TO-MONITOR字符,默认该字符为CTRL-X。当OPT_MON_TRAP选项有效时,输入TRAP-TO-MONITOR字符,将会终止通常的多任务系统并进入TRAP-TO-MONITOR程序。12.STATUStyIoctl(FASTTY_DEV_IDpTyDev,intrequest,intarg)处理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中指定的tyWrite(FASTTY_DEV_IDpTyDev,char*buffer,FASTintnbytes)向TY_DEV的发送环形缓冲区中写入数据。并启动tyTxStartup开始向device发送。注意在写入前take二进制信号量wrtSyncSem确保有足够的空间,写完后检查是否有空间,如果有空间则需要give二进制信号量wrtSyncSem,方便其他任务写入。14.inttyRead(FASTTY_DEV_IDpTyDev,char*buffer,intmaxbytes)从接收环形缓冲区中读取数据。pTyDev-lnBytesLeft变量记录了上次读取数据时,读取行中剩余的字节数,如果为0说明已经完整地读取了一整行。否则说明上次读取的行中还有一定的字节没有读取,这时候可以直接读取。读取完毕之后要检查接收环形缓冲区中空闲的空间否足够,如果足够的话且OPT_TANDEM选项有效且当前为XOFF,这时候需要向device发送一个XON信号表明接收准备好。15.STATUStyITx(FASTTY_DEV_IDpTyDev,char*pChar)中断级处理程序,取出下一个要发送的字符(注意并不是所有要发送的字符都是要先从发送环形缓冲区中取出来的,如XON/XOFF字符),准备将该字符发送到串口控制芯片(并未发送)。根据设定的规则,取出该字符需要根据如下准则:l首先检查接收端口是否需要发送XON/XOFF信号(需要发送的话rdState.pending为True),如果需要发送,则下一个发送的字符为XON/XOFF;l如果wrtState.xoff为true,说明发送处于关闭状态,此时是不能够发送数据的,因此直接退出并置pTyDev-wrtState.busy为FALSE;否则如果wrtState.flushingWrtB
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 第二单元第十六课《越算越精彩》说课稿 2024-2025学年粤教版(2019)初中信息技术八年级上册
- 2025公务用车购销合同虹口区
- 2025合同违约责任条款明确
- 2025网约车租赁合同书
- 第二单元《6.形色相随》说课稿-2024-2025学年浙人美版(2024)初中美术七年级下册
- 全国人教版初中信息技术八年级下册第一单元第3课《作点》说课稿
- 2025新住宅租赁合同(示范合同)
- 2025合同样例茶叶店合作经营合同律师起草专业版
- 1.3中华文明的起源说课稿2024~2025学年统编版七年级历史上册
- 五年级英语下册 Unit 2 My favourite season Part B第三课时说课稿2 人教PEP
- 住房供给调控预案
- 培训行业转介绍
- 文科物理(兰州大学)学习通网课章节测试答案
- 人教版高二数学(上)选择性必修第一册1.2空间向量基本定理【教学设计】
- catia考试图纸题目及答案
- pos机风险管理办法
- 2025年行业机器人边缘计算技术应用与场景分析
- 2025年安徽省公务员录用考试《行测》真题及答案
- 2025年加油站行业需求分析及创新策略研究报告
- 2025中国工业传感器行业市场白皮书
- 手机桌面市场深度解析
评论
0/150
提交评论