WinCE串口驱动分析
时间:04-11
来源:
点击:
COM_Close
COM_Close为与COM_Open相对应的操作。这期间的目的是释放COM_Open所使用的系统资源,除此以外如果在COM_Open期间创建了相应的IST还需要停止该线程,在最后将该HW_OPEN_INFO脱链。这样一来驱动状态就得以恢复。当然这期间还做了一写避免线程竞争的处理,使得代码看起来不是那么简单。
StartDispatchThread/StopDispatchThread
这两个函数都不是Stream所需要的标准接口,但却是中断服务程序所需的IST启动和关闭的手段,所以在这里顺便说一下。
StartDispatchThread函数用于启动IST,主要的工作为调用InterruptInitialize将系统中断与相应的事件联系起来。并启动SerialDispatchThread作为IST.其中调用了叫做 InterruptDone的函数,该函数会调用OAL中的OEMInterruptDone来完成中断的响应。
StopDispatchThread用于与StartDispatchThread相反的操作。停止的过程相对要复杂一些,该函数首先设定当前线程的优先级与分发线程相同,以便于在停止该线程的动作不会比释放内存的动作快以避免出错。停止的动作是让线程主动完成的,具体的方法是提交表示位KillRxThread然后通过Sleep请求调度,待到IST自己停止。这个时候由于IST已经停止所以在程序的最后调用InterruptDisable来屏蔽中断。
SerialDispatchThread/ SerialEventHandler
SerialDispatchThread/ SerialEventHandler就是串口驱动的中断分发程序(也就是IST的所在)。整个IST被分开写成两个部分---循环主体和事件处理程序。循环主体SerialDispatchThread内容相对比较简单,反复等待串口事件并调用SerialEventHandler对具体的中断进行处理,直到pSerialHead->KillRxThread被设置后退出。SerialEventHandler为中断处理的具体实现,程序在获得串口事件后运行,目的在于对中断进行进一步的判断并执行相应的处理。
下面参考两个结构体来完成接受和发送中断服务的分析。我们先来看RX_BUFFER_INFO结构。
typedef struct __RX_BUFFER_INFO {
ULONG Read; /* @field Current Read index. */
ULONG Write; /* @field Current Write index. */
ULONG Length; /* @field Length of buffer */
BOOL DataAvail; /* @field BOOL reflecting existence of data. */
PUCHAR RxCharBuffer; /* @field Start of buffer */
CRITICAL_SECTION CS; /* @field Critical section */
} RX_BUFFER_INFO, *PRX_BUFFER_INFO;
用该结构的原因是在驱动内部维护了一个缓冲器用作驱动和应用程序之间的缓冲.
在硬件内部已经有一个FIFO缓冲器,这保障了驱动的数据接收,但由于应用不一定能保障在驱动获得数据后及时将数据取走,因此在驱动内部维护了另外一个缓冲器。在RX_BUFFER_FIFO结构中的read成员为MDD取数据时在FIFO的位置标志,而PDD向软件写入数据的位标则对应被称作Write,DataAvail用作表示缓冲器内的数据是否有效。而RxCharBuffer则是指向软件FIFO的指针。当收到数据的时候就将write标示往上递增,而程序向驱动取数得时候Read递增,这样就可以根据Read和Write成员来维护这个FIFO。有了这个基本思路垫底我们接着看RX的中断服务具体如何实现。这间还会涉及到流控制的成分。
接收分支:在接收分支的开始计算软件缓冲器的剩余空间,如果有剩余的空间的话直接调用HWRxIntrHandler(PDDa实现)来从硬件缓冲区获取剩余空间大小的数据,若已无剩余空间则建立一个16byte的临时缓冲区,将数据读入该区域,实际上这个缓冲区在后面根本就不会被读取所以这些数据全部丢失掉了这也就自然需要统计硬件/软件缓冲导致的数据丢失(接收不及时导致)。接下来就是所谓XFlow的流程了,所谓XFlow就是我们上面提到的软件流控制。也就是用软件的方法来协调发送和接收端的收发,保障数据的完整接收。当接收到XOFF/XON标记的时候由于这个标记本身不数据数据而是控制标志,所以要讲后面的数据全部前置一位,覆盖掉XON/XOFF的位置。同时还需要根据标示的具体状态来设置DCB结构中的控制标示来控制数据收发流程。如果是XON标志,还需要在处理完接收流程后恢复发送流程。接收的动作会改变write标记的位置,这里需要重新计算该标示。至于硬件流控制的流程中该驱动模型是以缓冲器的75%为分位点来起停收发的,可用的硬件连线可以是DTR,也可以是RTS(模式相同仅仅是连线不同),这里的操作很简单,仅仅是通过计算缓冲器的存储状态来清除该标志就完成了硬件流控制的操作。由于在此过程中IST与其他部分是同步执行的,所以这个时候如果使用XFlow可能还会需要做一次安全检查。这样接收的流程就结束了。
发送分支: 我们同样来看看TX_BUFFER_INFO结构,看样子似乎该结构维护了一个和TX缓冲类似的缓冲区,但事实上这个缓冲区域是不独立存在的,发送的流程因为可以直接使用所需发送的数据的存储区域来作为发送缓冲,所以这个缓冲没有独立存在的必要。由于使用其它进程的数据区域,所以这里增加了权限控制项的成分,用于突破进程间的访问限制。
typedef struct __TX_BUFFER_INFO {
DWORD Permissions; /* @field Current permissions */
ULONG Read; /* @field Current Read index. */
ULONG Length; /* @field Length of buffer */
PUCHAR TxCharBuffer; /* @field Start of buffer */
CRITICAL_SECTION CS; /* @field Critical section */
} TX_BUFFER_INFO, *PTX_BUFFER_INFO;
下面来看看代码的具体内容。首先是对OpenCnt的检查,也就是设备是否被打开。若当会话已经关闭也就没有必要继续将数据送出了,并同时重置缓冲器的各个标志位。整个流程比较简单,在需要流控制时设置RTS或检查Xflow的情况后将数据送入硬件缓冲器.如果在没有数据需要发送的情况下简单的清除中断标示并发出发送结束事件就可以了。至于SetProcPermissions设置的目的在于获得访问其它线程数据空间的手段。
至于所谓的Modem和Line的情况则全部交给PDD来处理,我们后面再讨论。在这些全部都处理完了以后如果前面处理了接收,则发出接收(有可用的数据用于接收)的消息,让程序开始接收。后面还跟进了一个EvaluateEventFlag 函数,这个函数用于产生标准的Communication Events EV_RXFLAG,而且由于该驱动程序本身支持mult-open模式,所以需要将该事件送发到所有的实例中去。在ist期间有一些互锁、临界区的操作,因为不影响流程,同步化的内容这里我没有提。中断服务的分析大致就是如此,没有涉及到逻辑环节在后面的PDD部分再进行讨论。
Top