新聞中心
隨著計算機技術的不斷發(fā)展,串口接口在工業(yè)自動化、醫(yī)療設備、消費電子等領域得到了廣泛應用。而對于Linux操作系統(tǒng)而言,如何處理串口中斷則成為了一個需要解決的問題。

串口中斷是指通過串口傳輸數(shù)據(jù)時,接收端需要向發(fā)送端發(fā)送一些確認信息,以達到數(shù)據(jù)傳輸正確的目的。而在Linux下,處理串口中斷的方法分為以下兩種:
一、軟件方式處理串口中斷
在使用Linux進行串口通信時,可以利用系統(tǒng)調(diào)用(system call)機制要求系統(tǒng)為其注冊串口中斷服務程序,并利用此程序進行中斷處理。
具體而言,可以使用Linux內(nèi)核提供的”tty drivers”(tty驅動程序)來完成此項任務。 tty驅動程序可以從硬件中讀取串口數(shù)據(jù),接著將數(shù)據(jù)送到操作系統(tǒng)的中斷請求隊列中,由此觸發(fā)中斷服務程序進行處理。
不過值得注意的是,由于Linux中多個進程具有競爭關系的關系,這種方式很容易引起進程或線程阻塞等問題,需要進行深入的優(yōu)化。
二、硬件方式處理串口中斷
硬件方式處理串口中斷則是指,可以利用Linux內(nèi)核實現(xiàn)的interrupt(中斷)機制,直接利用操作系統(tǒng)中的工具“注冊中斷處理函數(shù)”并與串口接口聯(lián)系起來。
在進行硬件方式處理串口中斷時,也可以利用Linux內(nèi)核的I/O映射機制,將設備驅動程序與內(nèi)核建立聯(lián)系并實現(xiàn)中斷處理。
不過這種方式需要進行一些硬件層面的開發(fā)人員才能夠部署和調(diào)試。
無論采用軟件方式還是硬件方式,處理串口中斷對于Linux操作系統(tǒng)而言都是一個十分重要的任務。而對于工程師們而言,則需要根據(jù)具體的應用場景選擇戰(zhàn)略,并進行相關的程序設計和調(diào)試,以確保系統(tǒng)穩(wěn)定并能夠達到預期的數(shù)據(jù)傳輸目的。
成都網(wǎng)站建設公司-創(chuàng)新互聯(lián),建站經(jīng)驗豐富以策略為先導10多年以來專注數(shù)字化網(wǎng)站建設,提供企業(yè)網(wǎng)站建設,高端網(wǎng)站設計,響應式網(wǎng)站制作,設計師量身打造品牌風格,熱線:028-86922220求教,linux下網(wǎng)口虛擬串口驅動程序
開發(fā)虛擬串口驅動程序
虛擬串口就是當本地并沒有對應的串口硬件設備,而為應用層提供串口設備一樣的系統(tǒng)調(diào)用接口,以兼容原本使用本地串口的應用軟件的“虛”設備。本文作者給出了一種在Windows平臺上實現(xiàn)虛擬串口的方法,由此實現(xiàn)的“串口”具有真實串口完全相同的系統(tǒng)調(diào)用接口。
在很多應用中需要用到虛擬串口,如在Modem卡出現(xiàn)之前,已經(jīng)有了接在計算機串口上的外部Modem,而且各種拔號程序也是通過串口與外部Modem通信的。為了讓已有的拔號程序不做修改,像使用外部Modem一樣使用內(nèi)置卡,就需要內(nèi)置卡的驅動程序虛擬一個串口設備。又如當前工業(yè)界使用的一些串口服務器,缺枯往往有8個或16個甚至更多的串口,以連接多個串口設備,再通過一個網(wǎng)卡直接連入以太網(wǎng)。與它在同一網(wǎng)絡上的計算機就通過以太網(wǎng)與串口服務器上掛接的串口設備通信。為了讓計算機中原來使用本地串口的軟件兼容,就需要在計算機上提供虛擬串口驅動。
虛擬串口的設計關鍵在于,該“串口”實現(xiàn)后必須具有與真實串口完全相同的系統(tǒng)調(diào)用接口。要做到這點,從已有的串口設備驅動程序上做修改是更佳捷徑。下文就介紹以Windows NT上的串口驅動程序為基礎,開發(fā)可運行于Windows NT、Windows 2023、Windows XP的各個版本虛擬串口驅動程序。
串口驅動中使用的幾個鏈表
由于串口是雙工設備,在一個讀請求發(fā)出來還沒有完成之前,同時可以發(fā)出寫請求,加上在驅動程序層所有I/O請求都要求異步完成,即前一個請求尚沒有完成,下一個相同的請求可能又來了。為此,串口驅動程序需要使用多個雙向鏈表數(shù)據(jù)結構來處理各種IRP(I/O Request Packet,I/O請求包)。當收到一個IRP,先判斷是否可立即完成,可以馬上處理并返回,如果不允許則將IRP插在相應鏈表尾,在適當?shù)臅r候如設備有空閑時處理,這時往往會產(chǎn)生一個硬件中斷,激發(fā)DPC(Deferred Procedure Call,暫緩過程調(diào)用)過程,由DPC處理函數(shù)逐個從鏈表頭取出IRP并試著完成它。串口驅動中有以下幾個鏈表和DPC(在serial.h中有定義):
ReadQueue 和 CompleteReadDpc
用于保存Read IRP的鏈表和用于調(diào)度的DPC,與DPC對應的處理函數(shù)是SerialCompleteRead,它在read.c文件中,該函數(shù)的主要任務就是從ReadQueue中提取下一個IRP,并試著完成它。
WriteQueue 和 CompleteWriteDpc
用于保存Write IRP的鏈表和對應的DPC,與DPC對應的函數(shù)是SeriaCompleteWrite,它的實現(xiàn)在write.c中,該函數(shù)負責從WriteQueue中提取IRP,并試著完成它。
MaskQueue 和 CommWaitDpc
這一對鏈表用于處理Windows串口驅動的一個特性:事件驅動機制。它允許應用程序預設一個事件標志,而后等待與標志對應事件發(fā)生。DPC所調(diào)用的函數(shù)是SerialCompleteWait,它實現(xiàn)在Waitmask.c文件中,該函數(shù)也是試著從MaskQueue中提取IRP并完成它。
PurgeQueue
該鏈表與前面幾個稍有不同伏鍵洞,它沒有與之相對應的DPC機制,而是在每次收到Purge請求時從PurgeQueue中逐個提取IRP并試著完成,因某種原因不能完成時則插入鏈表。相應的函數(shù)是purge.c文件中的SerialStartPurge。
以上機制是串口驅動程序的重要實現(xiàn)方法,在虛擬串口驅動中需要保留,但不同的是,硬件串口驅動中是ISR(中斷服務程序)根據(jù)收、發(fā)或MODEM中斷來激發(fā)相應的DPC,而在虛擬串口驅動中將因實際情況不同會有不同的激發(fā)機制。
DriverEntry的實現(xiàn)
DriverEntry是驅動程序的入口函數(shù),相當于應用程序C語言中的main函數(shù),開發(fā)一個虛擬串口驅動亮悶首先要修改的就是它。它的函數(shù)實體在initunlo.c文件中。只是在虛擬串口驅動中由于不與具體的硬件打交道,就不存在硬件資源分析、硬件初始化、判斷其工作狀態(tài)等處理,只需要為虛擬串建立設備對象、符號鏈接和初始化數(shù)據(jù)結構。一個典型函數(shù)實現(xiàn)大體如下:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
/*填寫DriverObject->MajorFunction數(shù)組*/
/*建立設備對象*/
/*初始化SERIAL_DEVCIE_EXETENSION數(shù)據(jù)結構*/
Status = IoCreateDevice(DriverObject, sizeof(SERIAL_DEVICE_EXTENSION), &uniNameString, FILE_DEVICE_SERIAL_PORT, 0,TRUE,&deviceObject);
//初始化所有鏈表
InitializeListHead(&extension->ReadQueue);
InitializeListHead(…);
…;
//初始化所有DPC
KeInitializeDpc(&extension->CompleteReadDpc,SerailCompleteRead,extension);
KeInitializeDpc(…);
/*建立符號鏈接*/
SerialSetupExternalNaming(extension);
return Status;
}
SerialRead和SerialCompleteRead的實現(xiàn)
函數(shù)SerailRead和SerialCompleteRead決定了對Read IRP的響應策略,它們都存于read.c中。以串口服務器要用的虛擬串口為例,當串口服務器收到來自外部數(shù)據(jù)時將通過網(wǎng)絡發(fā)至計算機,計算機則產(chǎn)生相應的網(wǎng)絡中斷并進行協(xié)議數(shù)據(jù)處理。網(wǎng)絡接收線程緩存新收到的數(shù)據(jù)并激活CompleteReadDpc,從而SerialCompleteReadIrp得到調(diào)用,它再調(diào)用CompleteReadIrp對每個IRP進行處理。它們的實現(xiàn)大體如下:
NTSTATUS SerialRead(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
{
/*此處略去變量聲明和初始化*/
/*提取IRP中相關的數(shù)據(jù)*/
stack = IoGetCurrentIrpStackLocation(Irp);
ReadLen = stack->Parameters.Read.Length;
/*先看本地緩沖有數(shù)據(jù)否?有的話先讀取*/
if(Extension->InCounter > 0 )
{ //注意這里要加鎖,以防數(shù)據(jù)訪問沖突
KeAcquireSpinLock(&Extension->
ReadBufferLock,&lIrql);
FirstRead = (ReadLen>Extension->
InCounter)? Extension->InCounter: ReadLen;
RtlCopyMemory(Irp->AssociatedIrp.
SystemBuffer,Extension->pInBuffer,FirstRead);
Extension->InCounter -= FirstRead;
ReadLen -= FirstRead;
KeReleaseSpinLock(&Extension->
ReadBufferLock,lIrql);//釋放鎖
}
/*是否已讀到足夠數(shù)據(jù)?是的話則完成該IRP*/
if( 0 == ReadLen)
{
status=STATUS_SUCCESS;
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = FirstRead;
IoCompleteRequest(Irp,0);
return status;
}
/*沒有則將IRP插入隊列中,通過網(wǎng)絡向串口服務器發(fā)出讀數(shù)據(jù)請求*/
IoMarkIrpPending(Irp);
InsertWaitList(Extension->ReadQueue,Irp);
status = TdiSendAsync(Extension->ComChannel,pAckPacket,PacketLen(pAckPacket),(PVOID)ReadAckComplete,Irp);
/*返回PENDING,表示該IRP尚沒有完成*/
return STATUS_PENDING;
}
Void CompleteReadIrp(IN PSERIAL_DEVICE_EXTENSION extension,IN PIRP Irp,IN PUCHAR pInData,IN ULONG Length )
{
/*此處略去變量聲明和初始化*/
/*讀取新數(shù)據(jù)*/
ReadLen = (ReadLen > Length)? Length : ReadLen;
if(ReadLen != 0)
{
RtlCopyMemory(pReadAsync->
pReadBuffer,pInData,ReadLen);
pReadAsync->pReadBuffer += ReadLen;
pReadAsync->ReadAlready += ReadLen;
extension->PerfStats.ReceivedCount +=
ReadLen;
}
else
{
/*因為串口服務器端只有在已經(jīng)有了相應的數(shù)據(jù)或超過時間(此時,Length=0)才會發(fā)來應答并激活本DPC過程,所以此時已經(jīng)超時,為了便于結束本IRP,這里有意改變TotalNeedRead,造成接收完畢的假象*/
pReadAsync->TotalNeedRead =
pReadAsync->ReadAlready;
}
if(pReadAsync->TotalNeedRead == pReadAsync->ReadAlready)
{
/*該IRP是否已經(jīng)接收完畢,是的話則結束該
IRP*/
EndReadIrp(Irp);
/*從ReadQueue中取下一個IRP*/
}
/*本IRP沒有完成也沒有超時,則繼續(xù)等待本DPC下次被激活,注意此時要判斷IRP是否被要求取消*/
}
SerialWrite和SerailCompleteWrite的實現(xiàn)
SerialWrite和SerailCompleteWrite決定了Write IRP的實現(xiàn)。在SerialWrite中調(diào)用了網(wǎng)絡發(fā)送函數(shù)TdiSendAsync,當該發(fā)送完成后將激活CompleteWriteDpc,調(diào)度SerialCompleteWrite函數(shù),而它主要就是取出當前的WriteIRP,設置已經(jīng)發(fā)送的數(shù)據(jù)數(shù)量,調(diào)用CompleteWriteIrp做該IRP的進一步處理。它們大體如下:
NTSTATUS SerialWrite(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
{
/*此處略去變量聲明和初始化*/
/*從IRP中提取有關數(shù)據(jù)*/
stack=IoGetCurrentIrpStackLocation(Irp);
SendLen = stack->Parameters.Write.Length;
/*為網(wǎng)絡發(fā)送和異步操作分配緩沖,在CompleteWrite中全部數(shù)據(jù)發(fā)送完后釋放*/
pWriteAsync = ExAllocatePool(NonPagedPool,
SendLen+PACKET_HEADER_LEN+sizeof(WRITE_ASYNC));
if(pWriteAsync == NULL)
{
//錯誤處理
}
//保存異步數(shù)據(jù)
…
//設置網(wǎng)絡發(fā)送數(shù)據(jù)包
BuildDataPacket(pPacket,WRITE,(USHORT)SendLen,pWriteAsync->pWriteBuffer);
/*先將IRP暫時阻塞并插入隊列,在CompleteWrite中完成*/
IoMarkIrpPending(Irp);
InsertWaitList(extension->WriteQueue, Irp);
/*將寫請求和相關數(shù)據(jù)通過網(wǎng)絡發(fā)向串口服務器,由它負責將數(shù)據(jù)傳到具體串口設備*/
status = TdiSendAsync(Extension->ComChannel,pPacket,PacketLen(pPacket),(PVOID)CompleteWriteIrp,Irp);
//統(tǒng)計數(shù)據(jù)累加
Extension->PerfStats.TranittedCount += SendLen;
return STATUS_PENDING;
}
NTSTATUS CompleteWriteIrp(IN PDEVICE_OBJECT deviceobject,IN PIRP pIrp,IN PVOID context)
{
/*此處略去變量聲明和初始化*/
SendLen=pWriteAsync->TotalNeedWrite – pWriteAsync->WroteAlready;
if(SendLen == 0)//全部數(shù)據(jù)發(fā)送完畢
{
EndWaitWriteIrp(pWriteIrp,STATUS_SUCCESS,
pWriteAsync->WroteAlready,pWriteAsync);
//從WriteQueue中取下一個IRP;
}
else //發(fā)送剩余數(shù)據(jù)
{
if(pWriteIrp->Cancel)
{
//IRP被要求取消,完成WriteIrp
EndWaitWriteIrp(pWriteIrp,STATUS_CANCELLED,
pWriteAsync->WroteAlready,pWriteAsync);
return STATUS_CANCELED;
}
else
{
//再次設置網(wǎng)絡數(shù)據(jù)包并發(fā)送
BuildDataPacket(…);
status = TdiSendAsync(…);
//統(tǒng)計數(shù)據(jù)累加
Extension->PerfStats.TranittedCount +=
SendLen;
return STATUS_MORE_PROCESSING_REQUIRED;
}
}
}
其他幾個接口函數(shù)的實現(xiàn)
除Read/Write外,SerialUnload、SerialCreateOpen、 SerialClose、SerialCleanup、SerailFlush等調(diào)用接口是硬件相關性比較弱的接口函數(shù),基本不要修改,直接刪除原來操作硬件的部分即可。復雜一點就是SerialIoControl,該接口函數(shù)包含有大量設置、讀取串口硬件狀態(tài)的處理,可建立一個本地數(shù)據(jù)結構隨時保存虛擬串口的當前硬件狀態(tài)。同時為了保證串口服務器端的真實串口狀態(tài)和上層軟件要求的一致,需要將所有設置請求通過網(wǎng)絡發(fā)送到服務器端,由它負責改變真實硬件的狀態(tài)。
關于linux 串口中斷的介紹到此就結束了,不知道你從中找到你需要的信息了嗎 ?如果你還想了解更多這方面的信息,記得收藏關注本站。
創(chuàng)新互聯(lián)【028-86922220】值得信賴的成都網(wǎng)站建設公司。多年持續(xù)為眾多企業(yè)提供成都網(wǎng)站建設,成都品牌建站設計,成都高端網(wǎng)站制作開發(fā),SEO優(yōu)化排名推廣服務,全網(wǎng)營銷讓企業(yè)網(wǎng)站產(chǎn)生價值。
網(wǎng)站欄目:Linux下如何處理串口中斷?(linux串口中斷)
文章位置:http://fisionsoft.com.cn/article/cccesgi.html


咨詢
建站咨詢
