




已閱讀5頁,還剩290頁未讀, 繼續(xù)免費(fèi)閱讀
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
VxWorks驅(qū)動(dòng)開發(fā)筆記普通應(yīng)用軟件的開發(fā),客戶都會(huì)提出很明確的需求如功能、用戶界面、外部接口以及開發(fā)周期經(jīng)費(fèi)等等要求,這些要求一般都相對(duì)直觀且容易理解。但是對(duì)于驅(qū)動(dòng)程序的開發(fā)開說,開發(fā)周期以及經(jīng)費(fèi)這些需求往往都比較容易理解,可是對(duì)于功能、用戶界面以及外部接口等需求就很難描述了,因?yàn)檫@需要對(duì)底層操作系統(tǒng)的理解,否則就無法提出適宜的需求來,而對(duì)底層操作系統(tǒng)的理解才是驅(qū)動(dòng)程序開發(fā)之所以困難的主要原因。1.1驅(qū)動(dòng)程序的結(jié)構(gòu)驅(qū)動(dòng)程序有兩大基本特征:一是它實(shí)現(xiàn)了對(duì)硬件設(shè)備的訪問(最根本目的),二是它實(shí)現(xiàn)了一系列與硬件設(shè)備無關(guān)的的訪問接口。通過這些接口,上層軟件在控制此類硬件設(shè)備時(shí)無需對(duì)硬件進(jìn)行詳細(xì)的了解就可以進(jìn)行訪問,此外,當(dāng)硬件設(shè)備更換時(shí),只需要修改設(shè)備驅(qū)動(dòng)的硬件相關(guān)的部分,而上層軟件無需做任何更改。這兩個(gè)基本特征也正好決定了驅(qū)動(dòng)程序的主體結(jié)構(gòu)。如圖1.1所示,圖中的陰影部分為設(shè)備驅(qū)動(dòng)程序。圖1.1驅(qū)動(dòng)程序的結(jié)構(gòu)1.2驅(qū)動(dòng)程序的工作流程不同設(shè)備在操作系統(tǒng)中完成的工作是不同的,但是就是工作流程來說,大致可以分為兩個(gè)階段。第一個(gè)階段是初始化階段,在初始化階段,驅(qū)動(dòng)程序主要完成硬件以及設(shè)備驅(qū)動(dòng)相關(guān)數(shù)據(jù)結(jié)構(gòu)的初始化。第二個(gè)階段是硬件的訪問階段,根據(jù)設(shè)備工作模式的不同,可以分為中斷模式和輪詢模式,無論何種模式都可以通過與硬件設(shè)備無關(guān)的通用接口進(jìn)行硬件設(shè)備的訪問。2.1串口驅(qū)動(dòng)原理串口因?yàn)檎{(diào)試簡(jiǎn)單在許多數(shù)據(jù)量不大的場(chǎng)合依然較為流行,可以借助串口對(duì)目標(biāo)機(jī)中操作系統(tǒng)的運(yùn)行情況進(jìn)行監(jiān)控等等。下圖為Tornado開發(fā)軟件通過串口對(duì)目標(biāo)機(jī)上運(yùn)行的VxWorks操作系統(tǒng)進(jìn)行監(jiān)控的結(jié)構(gòu)原理圖。圖2.1Tornado通過串口對(duì)vxWorks操作系統(tǒng)進(jìn)行監(jiān)控設(shè)備的驅(qū)動(dòng)程序分為與硬件相關(guān)部分和硬件無關(guān)部分,而硬件相關(guān)部分則負(fù)責(zé)具體的硬件實(shí)現(xiàn),硬件無關(guān)部分實(shí)現(xiàn)了一系列通用的數(shù)據(jù)接口,其中硬件無關(guān)部分實(shí)現(xiàn)是create、remove、open、close、read、write、ioctl等7個(gè)通用的函數(shù)接口。使用這7個(gè)基本的函數(shù),不但能夠訪問串口,而且還能夠?qū)W(wǎng)絡(luò)、磁盤文件等多類設(shè)備進(jìn)行訪問。這7個(gè)函數(shù)的原型在文件ioLib.c中進(jìn)行定義。分別為:intcreat(constchar*name,intflag):該函數(shù)創(chuàng)建了一個(gè)文件描述符fd。其中name為一個(gè)抽象文件的文件路徑,這個(gè)抽象可以指硬盤上保存的一個(gè)文件,也可以是一個(gè)以字符串表示的一個(gè)設(shè)備,當(dāng)文件描述符創(chuàng)建完畢后自動(dòng)以參數(shù)flag打開該文件。STATUSremove(constchar*name):移除文件,name表明文件路徑。intopen(constchar*name,intflags,intmode);打開文件,準(zhǔn)備訪問。name為文件路徑,flags為打開方式如只讀、只寫、讀寫以及不存在可創(chuàng)建等,mode為打開模式只有在NFS文件系統(tǒng)下才有效。打開成功后返回文件描述符。STATUSclose(intfd);關(guān)閉文件。fd為文件描述符。intread(intfd,char*buffer,size_tmaxbytes);從文件描述符fd指定的文件中讀取最多maxbytes個(gè)字節(jié),保存在buffer指定的位置??赡軙?huì)由于文件長度的原因,實(shí)際讀取的字節(jié)數(shù)小于maxbytes,函數(shù)返回實(shí)際讀出的字節(jié)數(shù)。intwrite(intfd,char*buffer,size_tnbytes);向文件描述符fd指定的文件中寫入nbytes個(gè)字節(jié)的數(shù)據(jù),原數(shù)據(jù)保存在buffer只能的內(nèi)存中。可能會(huì)由于buffer空間等原因,實(shí)際寫入的字節(jié)數(shù)小于nbytes,函數(shù)返回實(shí)際寫入的字節(jié)數(shù)。intioctl(intfd,intfunction,intarg);控制函數(shù),主要用于設(shè)置或讀取設(shè)備的工作方式等特性。上述這7個(gè)通用接口,也就是串口驅(qū)動(dòng)的一個(gè)最根本的需求,即通過這幾個(gè)函數(shù)就可以實(shí)現(xiàn)對(duì)串口控制芯片i8250的操作。而函數(shù)庫iosLib中則是上述各個(gè)接口的較為底層的實(shí)現(xiàn)。它首先根據(jù)訪問設(shè)備類型的不同(普通磁盤文件和硬件設(shè)備)而將其分為兩類,并用不同的數(shù)據(jù)結(jié)構(gòu)描述,這里主要對(duì)結(jié)構(gòu)DRV_ENTRY進(jìn)行分析。如圖2.3。圖2.3iosLib庫提供的數(shù)據(jù)結(jié)構(gòu)結(jié)構(gòu)數(shù)組drvTable的每個(gè)元素drvTablei對(duì)應(yīng)一類設(shè)備,因此其下表i將是不同類型設(shè)備之間區(qū)分的一個(gè)重要的參考。由于0通常表示無效,因此drvTable0為空,不代表任何設(shè)備。圖2.4給出了ioLib提供的通用函數(shù)和結(jié)構(gòu)DRV_ENTRY的基本關(guān)系。圖中的箭頭表示調(diào)用關(guān)系,有調(diào)用者指向被調(diào)用者。圖2.4ioLib庫與iosLib庫的內(nèi)部關(guān)系由于數(shù)組drvTable的每個(gè)元素drvTablei對(duì)應(yīng)一類設(shè)備,如果要使用某一類設(shè)備中的一個(gè),如系統(tǒng)中兩個(gè)串口的一個(gè),就必須指明是哪個(gè)串口。在函數(shù)庫ioLib中的7個(gè)通用函數(shù)中都有一個(gè)重要的參數(shù)name,表明文件的路徑或者設(shè)備的名稱,該名稱具有唯一性,也就是一個(gè)name指向唯一一個(gè)文件或者唯一一個(gè)設(shè)備。下面要引入的數(shù)據(jù)結(jié)構(gòu)是設(shè)備描述符,如圖2.5所示,它在函數(shù)庫iosLib中定義。圖2.5設(shè)備描述符基本的設(shè)備描述符的數(shù)據(jù)結(jié)構(gòu)為DEV_HDR,它描述了一些最為基本的信息,數(shù)組decTable的下標(biāo)devnum和設(shè)備名稱name,該設(shè)備序號(hào)即是ioLib庫中使用的文件描述符fd。綜合起來設(shè)備描述符DRV_HDR、結(jié)構(gòu)DRV_ENTRY以及7個(gè)通用函數(shù)庫的關(guān)系可以用圖2.6來描述。圖2.6DRV_HDR、DRV_ENTRY及通用接口之間的關(guān)系上述通用接口可以滿足網(wǎng)卡、串口等外圍設(shè)備的需要,對(duì)于具體的外部設(shè)備,則需要對(duì)圖2.6所說的通用接口進(jìn)行擴(kuò)展,如圖2.7所示,這個(gè)擴(kuò)展主要采用了兩種手段,一是繼承,即通過對(duì)結(jié)構(gòu)DEV_HDR進(jìn)行繼承得到如串口、終端以及網(wǎng)卡等特殊類型的設(shè)備;而是多態(tài),也就是對(duì)一個(gè)通用的接口,根據(jù)具體設(shè)備的不同而采取不同的函數(shù)。C不是面向?qū)ο笳Z言,沒有明確的類、繼承和多態(tài)的概念,但是可以靈活地使用C達(dá)到同樣的目的。圖2.7通用接口通過多態(tài)和繼承得到擴(kuò)展圖2.8則更為詳細(xì)地描述了VxWorks操作系統(tǒng)對(duì)多個(gè)設(shè)備的管理。對(duì)每類設(shè)備的操作函數(shù),系統(tǒng)創(chuàng)建了一個(gè)DRV_ENTRY結(jié)構(gòu)數(shù)組來描述,每個(gè)DRV_ENTRY結(jié)構(gòu)變量對(duì)應(yīng)一類設(shè)備的操作函數(shù),對(duì)某類設(shè)備的具體操作方式的設(shè)置則是通過設(shè)定DRV_ENTRY結(jié)構(gòu)變量中個(gè)函數(shù)指針來實(shí)現(xiàn)的;對(duì)于同一類的設(shè)備,系統(tǒng)為其從DEV_HDR派生了一個(gè)數(shù)據(jù)結(jié)構(gòu),而每個(gè)結(jié)構(gòu)變量則是對(duì)應(yīng)該類設(shè)備中的每一個(gè)具體的設(shè)備,不同的設(shè)備通過雙向鏈表鏈接在一起。圖2.8VxWorks對(duì)系統(tǒng)中多個(gè)設(shè)備的管理結(jié)構(gòu)2.3串口驅(qū)動(dòng)程序函數(shù)庫分析為了進(jìn)一步加深對(duì)IO設(shè)備管理數(shù)據(jù)結(jié)構(gòu)的理解,本節(jié)將分別對(duì)函數(shù)庫ioLib、iosLib進(jìn)行分析。2.3.1函數(shù)庫ioLibioLib庫為上層提供了7個(gè)基本的函數(shù)接口:creat(),remove(),open(),close(),read(),write()以及ioctl()。上層用戶只需要對(duì)這7個(gè)函數(shù)進(jìn)行操作就能夠完成對(duì)硬件的訪問。下面一次分析ioLib庫中各個(gè)函數(shù)庫的功能。1.intcreat(constchar*name,intflag)該函數(shù)提供了一個(gè)與設(shè)備無關(guān)的通用接口,用于創(chuàng)建一個(gè)文件(可以是普通的磁盤文件也可以是抽象的設(shè)備文件),該文件的路徑為name,創(chuàng)建完畢后自動(dòng)打開,打開的參數(shù)為flag。該函數(shù)的正常返回值為文件描述符,否則將會(huì)返回ERROR。對(duì)照?qǐng)D2.8,該函數(shù)根據(jù)設(shè)備的名稱,從鏈表中找到該設(shè)備對(duì)應(yīng)的設(shè)備號(hào)drmNum(該數(shù)值即為函數(shù)返回的文件描述符),然后以此為下標(biāo)就可以找到該類設(shè)備的de_create函數(shù)指針,從而找到該設(shè)備的create函數(shù)。2.intopen(constchar*name,intflags,intmode)該函數(shù)打開一個(gè)文件以方便進(jìn)行讀、寫或者更新,打開后返回該文件的文件描述符。open()函數(shù)的參數(shù)為文件名以及訪問方式:lO_RDONLY(0)以只讀方式打開lO_WRONLY(1)以只寫方式打開lO_RDWR(2)以讀寫方式打開lO_CREAT(0x0200)如果文件不存在,就創(chuàng)建一個(gè)文件并打開。3.LOCALintioCreateOrOpen(constchar*name,intflags,intmode,BOOLcreate)這個(gè)函數(shù)其實(shí)是函數(shù)create()和open()函數(shù)的實(shí)現(xiàn)主體。create()和open()函數(shù)只是根據(jù)參數(shù)create簡(jiǎn)單調(diào)用函數(shù)ioCreateOrOpen()而已,真正的實(shí)現(xiàn)在函數(shù)ioCreateOrOpen()中。4.STATUSunlink(char*name)該函數(shù)主要是和posix兼容,它的功能與remove完全相同。5.STATUSremove(constchar*name)調(diào)用函數(shù)iosDelete來刪除文件。如果有符號(hào)鏈接,則需要沿著符號(hào)鏈接直接找到文件并刪除。6.STATUSclose(intfd)調(diào)用函數(shù)ioClose函數(shù)關(guān)閉文件。7.intrename(constchar*oldname,constchar*newname)修改文件名。并不是所有的設(shè)備都支持重命名操作,比如通常的dosFS和rt11FS都是支持重命名操作的,而netDrv和nfsDrv則不支持,因此在使用這個(gè)函數(shù)前需要明確設(shè)備是否支持重命名操作。重命名操作主要通過調(diào)用函數(shù)ioctl(fd,FIORENAME,(int)newname)完成。注意:調(diào)用函數(shù)ioctl(fd,FIORENAME,(int)newname)重命名前需要先打開文件open()。8.intread(intfd,char*buffer,size_tmaxbytes)調(diào)用函數(shù)iosRead實(shí)現(xiàn)讀操作,maxbytes為讀取的最大字節(jié)數(shù),讀取之后存放在buffer指定的地址空間,不過可能由于文件中字節(jié)數(shù)的限制等因素,實(shí)際讀取的字節(jié)數(shù)可能會(huì)小于maxbytes,因此需要在最終的返回值中返回實(shí)際讀取的字節(jié)數(shù)。9.intwrite(intfd,char*buffer,size_tnbytes)調(diào)用函數(shù)iosWrite向指定的文件中寫入數(shù)據(jù),nbytes為期望寫入的字節(jié)數(shù),但是實(shí)際上可能會(huì)由于文件本身的限制導(dǎo)致實(shí)際寫入的字節(jié)數(shù)小于nbytes,因此需要在返回值中記錄實(shí)際寫入的字節(jié)數(shù)。10.intioctl(intfd,intfunction,intarg)直接調(diào)用函數(shù)iosIoctl實(shí)現(xiàn)對(duì)文件的控制操作。11.intlseek(intfd,longoffset,intwhence)設(shè)定一個(gè)文件的讀寫指針,下次讀寫操作將從設(shè)定的位置開始。參數(shù)whence有三個(gè)數(shù)值:lSEEK_SET(0)設(shè)定到相對(duì)于文件起始位置偏移offset位置。lSEEK_CUR(1)設(shè)定到當(dāng)前位置偏移offset的位置。lSEEK_END(2)相對(duì)于文件結(jié)束位置偏移offset的位置。注意:如果指定的地址是無效的地址(超出了文件的范圍),那么將返回錯(cuò)誤指示。12.intreadv(intfd,structiovec*iov,intiovcnt)從設(shè)備fd中讀取數(shù)據(jù),保存在iov起始地址為iov的數(shù)組中。iovcnt為結(jié)構(gòu)iovec數(shù)組iov的元素的個(gè)數(shù)。iov是一個(gè)數(shù)組指針,它指向一個(gè)iovec結(jié)構(gòu)數(shù)組,iovec結(jié)構(gòu)中的元素iov_base指定了數(shù)據(jù)保存起始位置地址,而iovcnt則指定了iov數(shù)組的元素個(gè)數(shù),因此總的讀出的字節(jié)數(shù)為各個(gè)數(shù)組元素中保存數(shù)據(jù)的字節(jié)數(shù)之和。如圖2.9所示。圖2.9iovec結(jié)構(gòu)示意圖13.intwritev(intfd,registerstructiovec*iov,intiovcnt)該函數(shù)調(diào)用iosWrite函數(shù)將幾段分散的數(shù)據(jù)寫入到fd設(shè)備中。14.STATUSioFullFileNameGet(char* pathName,DEV_HDR* ppDevHdr,char* fullFileName)通常來說,一個(gè)完整的文件的路徑包括兩部分,該文件所在的設(shè)備路徑,以及該文件在該設(shè)備上的相對(duì)路徑,這兩部分組合得到了一個(gè)完整的路徑。這個(gè)函數(shù)的作用就是根據(jù)參數(shù)pathName制定的路徑名,這個(gè)路徑名可以是相對(duì)(相對(duì)麼默認(rèn)的路徑ioDefPath)路徑名或者絕對(duì)路徑名。函數(shù)ioFullFileNameGet的作用個(gè)就是根據(jù)提供的路徑名找到該文件坐在的設(shè)備及其相對(duì)于該設(shè)備的路徑。其中設(shè)備名結(jié)構(gòu)變量指針的指針保存在DEV_HDR*ppDevHdr參數(shù)中返回,相對(duì)于設(shè)備的文件名則存放在參數(shù)char* fullFileName中保存。舉例來說當(dāng)前默認(rèn)路徑為“/dev/sd1/x/”,相對(duì)路徑名為“y.txt”,該函數(shù)將會(huì)找到設(shè)備“/dev/sd1/”對(duì)應(yīng)的DEV_HDR*ppDevHdr指針返回,并將fullFileName設(shè)置為x/y.txt返回。15.STATUSioDefPathSet(char*name)設(shè)定默認(rèn)路徑,并將其復(fù)制到全局變量ioDefPath中,注意name必須是一個(gè)絕對(duì)路徑,也就是說name指定的路徑必須包含一個(gè)有效的設(shè)備(即從iosDevList中能夠搜索到的設(shè)備)。16.voidioDefPathGet(char*pathname)將ioDefPath復(fù)制到pathname中返回。17.STATUSchdir(char*pathname)這個(gè)函數(shù)等價(jià)于函數(shù)ioDefPathSet(),可以認(rèn)為是ioDefPathSet()函數(shù)的一個(gè)別名。18.char*getcwd(char*buffer,intsize)ioDefPathGet()函數(shù)的別名。只是如果參數(shù)size指定的空間太小的話將返回ERROR。19.char*getwd(char*pathname)ioDefPathGet()函數(shù)的別名。20.STATUSioDefPathCat(char*name)修改默認(rèn)的路徑。如果name是一個(gè)包含設(shè)備路徑的一個(gè)絕對(duì)路徑名,那么它就成為了一個(gè)新的默認(rèn)路徑。否則name將會(huì)被連接到當(dāng)前默認(rèn)路徑從而更新了新的默認(rèn)路徑。例如:如果默認(rèn)路徑為sda/x,name=sda/y,則默認(rèn)路徑修改為sda/y;如果name=/y,則默認(rèn)路徑修改為sda/x/y.21.voidioDefDevGet(char*devName)獲取默認(rèn)路徑所屬的設(shè)備的名稱。22.voidioDefDirGet(char*dirName)這個(gè)函數(shù)在新的版本中將會(huì)刪除。它的主要作用就是獲取默認(rèn)路徑相對(duì)于所屬設(shè)備的相對(duì)路徑。如設(shè)備名為sda,默認(rèn)路徑為sda/x,則ioDefDevGet()函數(shù)獲取了sda設(shè)備,而函數(shù)ioDefDirGet()則獲取了相對(duì)路徑。23.voidioGlobalStdSet(intstdFd,intnewFd)文件ioLib中定義了一個(gè)數(shù)組LOCALintioStdFd3,該數(shù)組表明標(biāo)準(zhǔn)文件描述符。這三個(gè)標(biāo)準(zhǔn)的文件描述符主要是:標(biāo)準(zhǔn)輸入0、標(biāo)準(zhǔn)輸出1、錯(cuò)誤輸出2。這個(gè)函數(shù)的作用,就是設(shè)置這個(gè)數(shù)組中某個(gè)元素的文件描述符。stdFd:指的是ioStdFd數(shù)組中元素的下標(biāo),02有效。newFd:數(shù)組中元素的數(shù)值。24.intioGlobalStdGet(intstdFd)獲取數(shù)組ioStdFd中某個(gè)元素的數(shù)值,即文件描述符的數(shù)值。25.voidioTaskStdSet(inttaskId,intstdFd,intnewFd)對(duì)VxWorks系統(tǒng)來說,并不是每個(gè)任務(wù)的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、錯(cuò)誤輸出都是相同的,多數(shù)任務(wù)的標(biāo)準(zhǔn)輸入是鍵盤,標(biāo)準(zhǔn)輸出是顯示器等,但是對(duì)某些串口監(jiān)控的任務(wù)來說,可以把它的標(biāo)準(zhǔn)輸入設(shè)置為串口等等。每個(gè)任務(wù)描述符中都有一個(gè)數(shù)組taskStd,表明這個(gè)任務(wù)的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出以及錯(cuò)誤輸出,這個(gè)函數(shù)的功能,就是設(shè)置一個(gè)任務(wù)的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、錯(cuò)誤輸出。26.intioTaskStdGet(inttaskId,intstdFd)獲取一個(gè)任務(wù)的標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、錯(cuò)誤輸出27.BOOLisatty(intfd/*filedescriptortocheck*/)判斷一個(gè)文件描述符是否是一個(gè)tty。最初的計(jì)算機(jī)沒有鍵盤和顯示器,輸入輸出都比較麻煩,后來有Teletype公司生產(chǎn)了一種字符型終端設(shè)備,因此有很多計(jì)算機(jī)操作系統(tǒng)都把字符型終端設(shè)備叫做tty設(shè)備。關(guān)于文件路徑的進(jìn)一步詳細(xì)操作可以參考函數(shù)庫pathLib。2.3.2函數(shù)庫iosLib函數(shù)庫iosLib是庫ioLib的底層實(shí)現(xiàn),它負(fù)責(zé)將各個(gè)硬件設(shè)備及其驅(qū)動(dòng)組織起來,從而使得用戶只需要調(diào)用ioLib庫就可以通過類似于文件IO的訪問方式來訪問各個(gè)硬件設(shè)備。在對(duì)iosLib庫函數(shù)進(jìn)行分析的過程中我們將主要分析系統(tǒng)是如何將這些設(shè)備以及驅(qū)動(dòng)組織起來,以及如何為ioLib庫提供接口的。在函數(shù)庫iosLib中定義了一個(gè)通用的接口結(jié)構(gòu)DRV_ENTRY,它為ioLib庫提供了一個(gè)通用過的接口,使得ioLib無需了解具體的設(shè)備類型就可以對(duì)硬件設(shè)備進(jìn)行訪問;另一方面它又通過繼承的方法,將不同類型的設(shè)備保存在鏈表中進(jìn)行統(tǒng)一管理(參見圖2.8)。下面對(duì)設(shè)備的管理函數(shù)進(jìn)行詳細(xì)分析。1.staticSTATUSnullWrite(intdummy,char*pBuf,intnBytes)空函數(shù)。2.STATUSiosInit(intmax_drivers,intmax_files,char*nullDevName)iosLib函數(shù)庫初始化,該函數(shù)的主要作用是動(dòng)態(tài)創(chuàng)建了一個(gè)FD_ENTRY結(jié)構(gòu)數(shù)組和一個(gè)DRV_ENTRY結(jié)構(gòu)數(shù)組,并進(jìn)行了初始化。3.intiosDrvInstall(FUNCPTRpCreate,FUNCPTRpDelete,FUNCPTRpOpen,FUNCPTRpClose,FUNCPTRpRead,FUNCPTRpWrite,FUNCPTRpIoctl)從DRV_ENTRY結(jié)構(gòu)數(shù)組中找到一個(gè)空閑的DRV_ENTRY結(jié)構(gòu)變量,將參數(shù)指定函數(shù)指針填充到該。注意由于最開始的一個(gè)元素即drvTable0中填充的是一個(gè)空設(shè)備,因此查找空閑的元素是從下表1開始查找的。如圖2.10。該函數(shù)返回下標(biāo)i。圖2.10函數(shù)iosDrvInstall的操作正是這個(gè)步驟才使得一類設(shè)備的特殊訪問方法與通用的接口連接起來。4.STATUSiosDrvRemove(intdrvnum,BOOLforceClose)函數(shù)iosDrvInstall的逆過程,將DRV_ENTRY結(jié)構(gòu)數(shù)組中的下表為drvnum的元素清空。如果forceClose參數(shù)為TRUE則需要強(qiáng)制關(guān)閉該設(shè)備已經(jīng)打開的文件。如果forceClose為FALSE但是該設(shè)備有打開的文件,則返回錯(cuò)誤。注意:一個(gè)設(shè)備可能對(duì)應(yīng)多個(gè)打開的文件。5.STATUSiosDevAdd(DEV_HDR*pDevHdr,char*name,intdrvnum)增加一個(gè)設(shè)備,該設(shè)備名稱(路徑)名為name,該設(shè)備的7個(gè)訪問函數(shù)在對(duì)應(yīng)的DRV_ENTRY結(jié)構(gòu)數(shù)組中的下表為drvnum。如圖2.11所示。注意,在添加設(shè)備之前需要調(diào)用函數(shù)iosDevMatch檢查已經(jīng)添加的設(shè)備中是否包含相同的路徑名(文件名)如果有則說明該設(shè)備已經(jīng)加入了。返回錯(cuò)誤。此外就是該函數(shù)只修改了iosDvList鏈,并不觸及DRV_ENTRY結(jié)構(gòu)數(shù)組的操作。如果已經(jīng)加入的設(shè)備中有“ab”這個(gè)名字,那么還可以增加名字為“abc”、“a”這樣的設(shè)備,但是名字為“ab”的再不能添加了。圖2.11在設(shè)備管理庫中加入新設(shè)備6.voidiosDevDelete(DEV_HDR*pDevHdr/*pointertodevicesstructure*/)從iosDvList鏈中刪除一個(gè)設(shè)備。注意添加的時(shí)候DEV_HDR結(jié)構(gòu)中的name是動(dòng)態(tài)分配的,因此這里需要釋放相應(yīng)的空間。這里可以參考iosDevAdd函數(shù),在iosDevAdd函數(shù)中參數(shù)pDevHdr也不是動(dòng)態(tài)分配的。7.DEV_HDR*iosDevFind(char*name,/*nameofthedevice*/char*pNameTail/*wheretoputptrtotailofname*/)根據(jù)名字查找一個(gè)設(shè)備的DEV_HDR結(jié)構(gòu)。查找遵照一下原則:l調(diào)用函數(shù)iosDevMatch在devTable數(shù)組中查找與name最為匹配的,也就是說查找的設(shè)備名字與name的前面n(n為設(shè)備名稱的字符個(gè)數(shù))個(gè)字符完全相同;l如果查到了匹配的,則返回值*pNameTail返回的則是name字符串中不包含設(shè)備名稱的字符串;l如果沒有找到匹配的設(shè)備,則返回默認(rèn)的設(shè)備(如果有的話),此時(shí)*pNameTail返回的則是原name字符串。8.LOCALDEV_HDR*iosDevMatch(char*name)該函數(shù)的作用是查找已經(jīng)添加的設(shè)備名中與參數(shù)name匹配度最大設(shè)備名。所謂匹配度最大就是說設(shè)備名正好是name字符串的前n(n為設(shè)備名稱字符串長度)個(gè)字符完全相同中的所有設(shè)備中n最大的那一個(gè)設(shè)備。例如已經(jīng)添加的設(shè)備中有“ab”、“abc”“bcde”,那么與name=“abcde”匹配最大的為“abc”。9.DEV_HDR*iosNextDevGet(DEV_HDR*pDev)從node鏈中查找下一個(gè)node的DEV_HDR結(jié)構(gòu)變量,參照前面的鏈表結(jié)構(gòu)圖。10.intiosCreate(DEV_HDR*pDevHdr,char*fileName,intmode)該函數(shù)根據(jù)DEV_HDR結(jié)構(gòu)指針pDevHdr中的drvNum數(shù)值作為下標(biāo),找到drvTable數(shù)組中的DEV_ENTRY結(jié)構(gòu)變量中的de_create函數(shù)指針,調(diào)用de_create函數(shù)。11.intiosDelete(DEV_HDR*pDevHdr,char*fileName)類似于函數(shù)iosCreate()。12.intiosOpen(DEV_HDR*pDevHdr,char*fileName,intflags,intmode)類似于函數(shù)iosCreate()。13.STATUSiosClose(intfd)類似于函數(shù)iosCreate()。14.intiosRead(intfd,char*buffer,intmaxbytes)類似于函數(shù)iosCreate()。15.intiosWrite(intfd,char*buffer,intnbytes)類似于函數(shù)iosCreate()。16.intiosIoctl(intfd,intfunction,intarg)基本的控制函數(shù),主要完成幾個(gè)功能。function=FIOGETNAME,將文件描述符的名字復(fù)制到arg中;其他情況則執(zhí)行drvTablepFdEntry-pDevHdr-drvNum.de_ioctl,這個(gè)類似于函數(shù)iosCreate()。17.LOCALvoidiosLock(void)這個(gè)函數(shù)和iosUnlock共同管理一個(gè)二進(jìn)制信號(hào)量,主要負(fù)責(zé)對(duì)fdTable、iosDevList的訪問保護(hù)。主要進(jìn)行互斥操作。2.3.3函數(shù)庫ttyDrv.c前面主要分析了vxWorks系統(tǒng)IO設(shè)備管理的通用數(shù)據(jù)結(jié)構(gòu)及其操作,這個(gè)通用接口既適用于串口類設(shè)備、又適用于網(wǎng)絡(luò)設(shè)備等等。對(duì)于串口這類設(shè)備來說,其驅(qū)動(dòng)又有其獨(dú)有的要求,如收發(fā)緩存的管理,波特率的設(shè)置等等。同樣,作為串口驅(qū)動(dòng)的部分,也可以分為與硬件相關(guān)部分和與硬件無關(guān)部分。VxWorks操作系統(tǒng)的串口驅(qū)動(dòng)與硬件無關(guān)部分主要有兩個(gè)函數(shù)庫來實(shí)現(xiàn),一是ttyDrv函數(shù)庫,主要用于7個(gè)通用函數(shù)中的5個(gè)函數(shù)的具體實(shí)現(xiàn),另一個(gè)是tyLib函數(shù)庫,主要用于處理收發(fā)緩沖區(qū)的處理。另一方面,從結(jié)構(gòu)TYCO_DEV來看,tyLib的主要數(shù)據(jù)結(jié)構(gòu)TY_DEV變量包含在結(jié)構(gòu)TYCO_DEV,因此其層次關(guān)系可以用圖2.12所示。圖2.12函數(shù)庫ttyDrv與tyLib的層次關(guān)系函數(shù)庫ttyDrv主要通過結(jié)構(gòu)SIO_CHAN訪問底層函數(shù),在結(jié)構(gòu)TYCO_DEV中有一個(gè)SIO_CHAN結(jié)構(gòu)變量指針,該變量指針指向底層的操作函數(shù)。如圖13所示。圖2.13ttyDrv庫與底層庫的函數(shù)接口首先分析ttyDrv函數(shù)庫,這個(gè)函數(shù)庫比較簡(jiǎn)單,直接分析各個(gè)函數(shù)。1.STATUSttyDrv(void)函數(shù)ttyDrv主要調(diào)用了函數(shù)iosDrvInstall完成了7個(gè)函數(shù)的安裝,對(duì)串口來說,系統(tǒng)并不要完全安裝這7個(gè)函數(shù),比如delete函數(shù),如果操作系統(tǒng)不要求delete串口驅(qū)動(dòng),那么這個(gè)函數(shù)也就不需要了。實(shí)際上ttyDrv函數(shù)主要安裝了基本操作函數(shù),如圖2.14。圖2.14函數(shù)ttyDrv主要安裝的函數(shù)這些函數(shù)中有ttyOpen、ttyClose、ttyIoctl是由函數(shù)庫ttyDrv.C提供的,其他函數(shù)如tyRead、tyWrite則是由函數(shù)庫tyLib提供的。2.STATUSttyDevCreate(char*name,SIO_CHAN* pSioChan,intrdBufSize,intwrtBufSize)這個(gè)函數(shù)為一個(gè)tty設(shè)備創(chuàng)建了一個(gè)TYCO_DEV數(shù)據(jù)結(jié)構(gòu)并進(jìn)行初始化(包括硬件的初始化)。其中pSioChain是底層串行通道描述符(SIO_CHAN結(jié)構(gòu)變量)的地址。rdBufSize和wrtBufSize為讀寫緩沖區(qū)的大小。ttyDevCreate函數(shù)在執(zhí)行過程中調(diào)用了函數(shù)tyDevInit對(duì)tyLib庫進(jìn)行了初始化。注意調(diào)用函數(shù)tyDevInit的最后一個(gè)參數(shù)ttyStartup,這個(gè)函數(shù)對(duì)底層操作函數(shù)(pSioChan)-pDrvFuncs-txStartup進(jìn)行了封裝,也就是說只有函數(shù)ttyDrv直接與底層硬件操作函數(shù)向關(guān)聯(lián),而tyLib庫則完全被保護(hù)在了硬件無關(guān)層(參見圖2.12)。ttyDevCreate()函數(shù)操作的數(shù)據(jù)結(jié)構(gòu)如圖2.15所示。同時(shí)它指定了串口的處理模式為中斷模式(另一種為輪詢模式)。圖2.15ttyDevCreate()函數(shù)操作的數(shù)據(jù)結(jié)構(gòu)注意這個(gè)函數(shù)和ioLib庫中的create函數(shù)是完全不同的概念,該函數(shù)的調(diào)用應(yīng)該在create之前。3.LOCALintttyOpen(TYCO_DEV* pTyCoDev,char* name,int flags,int mode)打開一個(gè)ttyDrv串行設(shè)備。所謂打開設(shè)備就是使其處于準(zhǔn)備收發(fā)數(shù)據(jù)的狀態(tài),對(duì)i8250串口控制芯片來說就是設(shè)定計(jì)算機(jī)準(zhǔn)備接收(DTR)和請(qǐng)求發(fā)送數(shù)據(jù)(RTS)的狀態(tài)。注意:每調(diào)用一次函數(shù)ttyOpen,該pTyCoDev-tyDev.numOpen就會(huì)自動(dòng)加1,表明該設(shè)備打開的次數(shù)。4.LOCALintttyClose(TYCO_DEV* pTyCoDev)關(guān)閉一個(gè)ttyDrv設(shè)備。所謂關(guān)閉設(shè)備就是就是關(guān)閉其收發(fā)數(shù)據(jù)就緒的狀態(tài)。注意:每調(diào)用一次函數(shù)ttyClose,pTyCoDev-tyDev.numOpen就會(huì)自動(dòng)減1,表明該設(shè)備打開的次數(shù)。5.LOCALintttyIoctl(TYCO_DEV* pTyCoDev,int request,void* arg)對(duì)硬件設(shè)備的控制操作。request是一個(gè)比較廣義的操作,包含的種類繁多,而且對(duì)同樣的操作,對(duì)不同的函數(shù)庫來說,參數(shù)也可能會(huì)有所不同,如波特率設(shè)置,在ttyDrv庫request為FIOBAUDRATE,這個(gè)參數(shù)在iosLib.h中定義,而在sioLib庫中該參數(shù)為SIO_BAUD_SET,因此需要進(jìn)行參數(shù)的轉(zhuǎn)換;另一方面sioIoCtl函數(shù)和tyIoCtl函數(shù)處理的職責(zé)是不同的,因此需要首先嘗試調(diào)用sioIoCtl函數(shù),如果無此操作則嘗試tyIoCtl函數(shù)。6.LOCALvoidttyStartup(TYCO_DEV*pTyCoDev)發(fā)送數(shù)據(jù)函數(shù)。直接調(diào)用底層的發(fā)送函數(shù)。在函數(shù)ttyDrv中調(diào)用iosDrvInstall函數(shù)安裝了兩個(gè)函數(shù)tyRead和tyWrite,這兩個(gè)函數(shù)并在函數(shù)庫ttyDev中,而是在tyLib庫中。主要完成收發(fā)緩沖區(qū)的處理,下面要進(jìn)行tyLib庫的分析。2.3.4函數(shù)庫tyLibtyLib函數(shù)在ttyDrv庫與底層硬件操作之間建立了一個(gè)收發(fā)緩沖區(qū),當(dāng)上層函數(shù)需要接收數(shù)據(jù)時(shí)并不是直接讀取硬件的接收寄存器,而是調(diào)用函數(shù)tyRead讀取tyLib的緩沖區(qū),同樣在發(fā)送數(shù)據(jù)時(shí)也是通過調(diào)用tyWrite函數(shù)將數(shù)據(jù)發(fā)送給tyLib的緩沖區(qū)中。tyLib負(fù)責(zé)對(duì)緩沖區(qū)進(jìn)行管理,包括在輪詢和中斷模式下自動(dòng)從串口控制器中讀寫數(shù)據(jù),當(dāng)接收緩沖區(qū)占用了一定的空間之后,調(diào)用上層回調(diào)函數(shù)將接收緩沖區(qū)的數(shù)據(jù)取走等等,另外在不同的模式下,還要完成對(duì)接收緩沖區(qū)的數(shù)據(jù)進(jìn)行重新組織等任務(wù)。當(dāng)前tyLib庫的接收緩沖區(qū)管理主要實(shí)現(xiàn)了line模式和raw模式的緩沖區(qū)管理。所謂line模式就想當(dāng)我們的中斷輸入,只有在按下回車鍵以后,計(jì)算機(jī)才進(jìn)行相應(yīng)的動(dòng)作,否則只是在屏幕上進(jìn)行顯示,如果在動(dòng)作前按下了退格、CTRL-X、CTRL-C等等按鍵,那么還要重新對(duì)新輸入的line進(jìn)行修改等等。這在利用串口連接終端的情況下是十分有用的。在raw模式下,計(jì)算機(jī)每次指示讀取相應(yīng)的字符,并不對(duì)其中的含義進(jìn)行解釋,讀取完畢后就根據(jù)緩沖區(qū)的情況決定是否調(diào)用回調(diào)函數(shù)將接收緩沖區(qū)的數(shù)據(jù)取走。1.STATUStyDevInit下面分析tyLib庫中的各個(gè)函數(shù)。(FASTTY_DEV_IDpTyDev,intrdBufSize,intwrtBufSize,FUNCPTRtxStartup)用指定的參數(shù)初始化tty設(shè)備描述符。tty設(shè)備描述符是一個(gè)數(shù)據(jù)結(jié)構(gòu)TY_DEV的變量而不是硬件設(shè)備,該結(jié)構(gòu)變量在函數(shù)調(diào)用前已經(jīng)創(chuàng)建,因此這里只需要進(jìn)行初始化就行了,而不用重新創(chuàng)建。注意區(qū)分互斥變量和二進(jìn)制信號(hào)量的區(qū)別。互斥變量的目的是為了保證在一定的時(shí)間段內(nèi)一個(gè)任務(wù)對(duì)某個(gè)資源獨(dú)占,即只有當(dāng)前任務(wù)可以訪問某一資源,在這期間其他任務(wù)無法獲取到該資源,互斥變量的獲取和釋放都是由同一任務(wù)完成,它主要強(qiáng)調(diào)資源的獨(dú)占性;而二進(jìn)制信號(hào)量則是某一任務(wù)為了獲取某一資源,但是該資源卻是由其他任務(wù)來提供的,它的主要目的是為了確保不同任務(wù)的同步,即只有在資源有效的情況下,才能有任務(wù)實(shí)用該資源,二進(jìn)制信號(hào)量的獲取和提供往往是由不同的任務(wù)來完成的,通常是任務(wù)A提供了某一資源,任務(wù)B等待到任務(wù)A提供了資源后才能有效獲取。二者的區(qū)別主要在于二進(jìn)制信號(hào)量的資源是消耗性的,它分別由不同的任務(wù)生產(chǎn)和消耗;而互斥變量保護(hù)的資源則是獨(dú)占性,不存在生產(chǎn)和消耗之說,而僅僅是確保只有一個(gè)任務(wù)可以訪問該資源。這里的二進(jìn)制信號(hào)量rdSyncSem,這個(gè)信號(hào)量確保底層串口驅(qū)動(dòng)tyIRd在輸入環(huán)狀緩沖區(qū)保存了一定的數(shù)據(jù)之后,tyRead上層函數(shù)才能有效獲取該信號(hào)量并開始讀取這些數(shù)據(jù)。二進(jìn)制信號(hào)量wrtSyncSem則是確保在輸出環(huán)狀緩沖區(qū)中有足夠的空間資源,當(dāng)有了足夠的空間資源則函數(shù)tyITx釋放掉一定的空間時(shí)候信號(hào)量有效,而tyWrite只有在獲取了該信號(hào)量后才可以向輸出環(huán)狀緩沖區(qū)中寫入數(shù)據(jù)?;コ庾兞縨utexSem的作用是為了確保對(duì)TY_DEV結(jié)構(gòu)變量的訪問保護(hù)。2.STATUStyDevRemove(TY_DEV_IDpTyDev)tyDevInit函數(shù)的逆過程。注意這里flushingRdBuf和flushingWrtBuf要在rdBuf和wrtBuf內(nèi)存被釋放之前置為True是為了確保中斷處理程序tyITx和tyIRd不會(huì)在釋放了之后訪問這段內(nèi)存。3.LOCALvoidtyFlush(FASTTY_DEV_IDpTyDev)這個(gè)函數(shù)比較簡(jiǎn)單,直接調(diào)用函數(shù)tyFlushRd和函數(shù)tyFlushWrt對(duì)各自的buffer進(jìn)行flush。4.LOCALvoidtyFlushRd(FASTTY_DEV_IDpTyDev)該函數(shù)的作用是對(duì)輸入環(huán)狀緩沖區(qū)進(jìn)行flush。注意flush前需要將flushingRdBuf置位是為了確保不被中斷處理程序tyIRd搞亂。注意flush結(jié)束之后要發(fā)送一個(gè)XON信號(hào)提示對(duì)方串口設(shè)備接收準(zhǔn)備好,并將變量flushingRdBuf復(fù)位為False。5.LOCALvoidtyFlushWrt(FASTTY_DEV_IDpTyDev)該函數(shù)的作用是對(duì)輸出環(huán)狀緩沖區(qū)進(jìn)行flush。注意flush前需要將flushingWrtBuf置位是為了確保不被中斷處理程序tyITx搞亂。注意flush結(jié)束之后free空間很大,因此要給出一個(gè)wrtSyncSem信號(hào)量,并將變量flushingWrtBuf復(fù)位為False。最后還要利用_func_selWakeupAll通知各個(gè)函數(shù)輸出環(huán)狀緩沖區(qū)已經(jīng)空出來了。6.voidtyAbortFuncSet(FUNCPTRfunc)設(shè)置abort函數(shù),當(dāng)收到abort字符時(shí)執(zhí)行func函數(shù)。默認(rèn)為null。7.voidtyAbortSet(charch)設(shè)置abort字符,默認(rèn)該字符為CTRL-C。8.voidtyBackspaceSet(charch)設(shè)定backspace字符,默認(rèn)字符為CTRL-H。9.voidtyDeleteLineSet(charch)設(shè)置行刪除命令字符,默認(rèn)為CTRL-U。10.voidtyEOFSet(charch)設(shè)置end-of-file字符,默認(rèn)該字符為CTRL-D。11.voidtyMonitorTrapSet(charch)設(shè)置TRAP-TO-MONITOR字符,默認(rèn)該字符為CTRL-X。當(dāng)OPT_MON_TRAP選項(xiàng)有效時(shí),輸入TRAP-TO-MONITOR字符,將會(huì)終止通常的多任務(wù)系統(tǒng)并進(jìn)入TRAP-TO-MONITOR程序。12.STATUStyIoctl(FASTTY_DEV_IDpTyDev,intrequest,intarg)處理tty設(shè)備的ioctl請(qǐng)求?,F(xiàn)有的bug:在line模式下,以參數(shù)FIONREAD調(diào)用返回的是輸入緩沖區(qū)中的字符數(shù),如果在buffer中有5個(gè)空行,每個(gè)空行字符占用一字節(jié),此外還有一個(gè)行字符數(shù)占用一字節(jié),那么其返回值為10。FIONREAD:返回接收環(huán)形緩沖區(qū)中的有效字符數(shù)(不包含正在輸入的未完成的行)。FIONWRITE:返回發(fā)送環(huán)形緩沖區(qū)中的有效字符數(shù)。FIOFLUSH:flush輸入發(fā)送環(huán)形緩沖區(qū)。FIOWFLUSH:flush接收環(huán)形緩沖區(qū)。FIORFLUSH:flush發(fā)送環(huán)形緩沖區(qū)。FIOGETOPTIONS:讀取設(shè)備選項(xiàng)。FIOSETOPTIONS:設(shè)置設(shè)備選項(xiàng)。FIOCANCEL:取消設(shè)備的讀寫。FIOISATTY:返回TRUE。FIOPROTOHOOK:設(shè)定函數(shù)pTyDev-protoHook。FIOPROTOARG:設(shè)定函數(shù)pTyDev-protoHook的參數(shù)pTyDev-protoArg。FIORBUFSET:重新設(shè)定發(fā)送環(huán)形緩沖區(qū)。FIOWBUFSET:重新設(shè)定發(fā)送環(huán)形緩沖區(qū)。FIOSELECT:增加selWakeupList。FIOUNSELECT:刪掉selWakeupList中指定的tyWrite(FASTTY_DEV_IDpTyDev,char*buffer,FASTintnbytes)向TY_DEV的發(fā)送環(huán)形緩沖區(qū)中寫入數(shù)據(jù)。并啟動(dòng)tyTxStartup開始向device發(fā)送。注意在寫入前take二進(jìn)制信號(hào)量wrtSyncSem確保有足夠的空間,寫完后檢查是否有空間,如果有空間則需要give二進(jìn)制信號(hào)量wrtSyncSem,方便其他任務(wù)寫入。14.inttyRead(FASTTY_DEV_IDpTyDev,char*buffer,intmaxbytes)從接收環(huán)形緩沖區(qū)中讀取數(shù)據(jù)。pTyDev-lnBytesLeft變量記錄了上次讀取數(shù)據(jù)時(shí),讀取行中剩余的字節(jié)數(shù),如果為0說明已經(jīng)完整地讀取了一整行。否則說明上次讀取的行中還有一定的字節(jié)沒有讀取,這時(shí)候可以直接讀取。讀取完畢之后要檢查接收環(huán)形緩沖區(qū)中空閑的空間否足夠,如果足夠的話且OPT_TANDEM選項(xiàng)有效且當(dāng)前為XOFF,這時(shí)候需要向device發(fā)送一個(gè)XON信號(hào)表明接收準(zhǔn)備好。15.STATUStyITx(FASTTY_DEV_IDpTyDev,char*pChar)中斷級(jí)處理程序,取出下一個(gè)要發(fā)送的字符(注意并不是所有要發(fā)送的字符都是要先從發(fā)送環(huán)形緩沖區(qū)中取出來的,如XON/XOFF字符),準(zhǔn)備將該字符發(fā)送到串口控制芯片(并未發(fā)送)。根據(jù)設(shè)定的規(guī)則,取出該字符需要根據(jù)如下準(zhǔn)則:l首先檢查接收端口是否需要發(fā)送XON/XOFF信號(hào)(需要發(fā)送的話rdState.pending為True),如果需要發(fā)送,則下一個(gè)發(fā)送的字符為XON/XOFF;l如果wrtState.xoff為true,說明發(fā)送處于關(guān)閉狀態(tài),此時(shí)是不能夠發(fā)送數(shù)據(jù)的,因此直接退出并置pTyDev-wrtState.busy為FALSE;否則如果wrtState.flushingWrtBuf為true,則說明正在清空
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 微生物檢驗(yàn)技術(shù)的未來發(fā)展方向試題及答案
- 項(xiàng)目管理資格考試的多維度考察及試題答案
- 微生物檢驗(yàn)技師證書考試的實(shí)踐反思與試題
- 特許金融分析師考試股市分析試題及答案
- 2025年證券從業(yè)資格證考試完整試題及答案
- 2025年考試復(fù)習(xí)時(shí)應(yīng)關(guān)注的知識(shí)點(diǎn)試題及答案
- 項(xiàng)目管理中的客戶滿意度提升策略試題及答案
- 學(xué)習(xí)風(fēng)格與注冊(cè)會(huì)計(jì)師考試成績(jī)提升的關(guān)系分析試題及答案
- 一年級(jí)數(shù)學(xué)下冊(cè) 四 牧童-認(rèn)識(shí)圖形教學(xué)設(shè)計(jì) 青島版六三制
- 項(xiàng)目管理中的人際交往技巧試題及答案
- 人教版數(shù)學(xué)七年級(jí)上冊(cè)1.2.2《數(shù)軸》訓(xùn)練習(xí)題(有答案)
- 中水回用機(jī)房設(shè)備安裝
- 建筑工程《擬投入本項(xiàng)目的主要施工設(shè)備表及試驗(yàn)檢測(cè)儀器設(shè)備表》
- (新版)制絲操作工(二級(jí))理論考試復(fù)習(xí)題庫-上(單選題匯總)
- 醫(yī)院醫(yī)保季度分析報(bào)告總結(jié)
- 新生兒口腔運(yùn)動(dòng)干預(yù)
- 波司登品牌營銷方案
- 滅火器售后服務(wù)承諾書
- 《臨床營養(yǎng)學(xué)》課件
- 《中國古代都城》課件
- 被執(zhí)行人生活費(fèi)申請(qǐng)書范文
評(píng)論
0/150
提交評(píng)論