转载BDA驱动学习笔记(1--3)

  BDA驱动学习笔记(1):功能拓扑图

功能拓扑图(Functional Topology)BAD的目标是不管硬件或者接收信号如何变,它的整体架构可以不做大调整,为此BDA引入功能拓扑图的概念,它把类似DirectShow Filter的结构作为节点(Node),包含在一个receiver filter中,因此一个filter可以做很多工作,这取决于该filter包含了哪些node,以及这些node都具备哪些功能。Functional Topology只是描述了一个结构,具体工作分发给了拓扑图中的各个node。在我们的程序中,外部可见的一个filtertunner filter,实际上包含了两个nodetuner demodulator。拓扑图中的node不是随便什么类型都可以的,只有在BDA Node Category GUIDs列表中列明的node类型,才可以加到图中去,node的各项功能也都是用注册分发例程的方法完成。

BDA Node Category GUIDs

KSNODE_BDA_RF_TUNER

KSNODE_BDA_QAM_DEMODULATOR

KSNODE_BDA_QPSK_DEMODULATOR

KSNODE_BDA_8VSB_DEMODULATOR

KSNODE_BDA_OPENCABLE_POD

KSNODE_BDA_PID_FILTER

KSNODE_BDA_IP_SINK

每种node都有特定的GUID,当network provider filter连上tunner filter时,它会根据GUID寻找相应的node并生成实例。Tunner filter中还使用Template topology 来描述各个PIN NODE的类型以及连接方式等信息。

转载BDA驱动学习笔记(1--3)_第1张图片
 

如上图所示,整幅图是一个Functional Topologytunner node demodualator node是两个node,它们都有特定的GUID,他们都是在network provider filter连上tunner filter时动态生成的。pin1tunner node相连而不和demodulator相连,tunner node demodulator node之前而不是之后等等信息是Template topology决定好的,整个图的连接也是在network provider filter连上tunner filter时根据Template topology动态完成的。

不过一个filter里包含几个node不是硬性规定的,比如上面这幅图,可以是一个filter里包含两个node,也可以拆分成两个filter,每个filter包含一个node,看自己喜欢。

我们常用的nodeNetwork ProviderTunerDemodulatorCaptureNetwork Provider目前有很多现成的filter可用,可能做DMB的时候需要去修改这部分,大部分时候直接拿来用就可以;tunner demodulator现在是放在同一个filter里,叫tunner filtercapture filter是最核心的部分,负责从硬件读取数据和往后传数据,一般单独做成一个filterBDA还提供了很多其他的node,帮我们做了很多工作。比如PID Filter NODE 实际上做的是demultiplexer filter的工作,不过目前为止我都不用PID Filter NODE,而是用DirectShow中的demultiplexer filter

BDA驱动学习笔记(2):例程注册

   

BDA minidriver的职责:调制(tunning)信号,解调(demodulating)信号,获取(capture),分流(demultiplexing)。BDA是在AVStream上的扩展,Bdasup.lib提供BDA的属性集和方法集。写BDA驱动的主要工作就是把Bdasup.lib中要求的属性集方法集的例程都注册一遍(需要的注册,确实不需要的也可以不管),提供自己的实现。

BDA驱动的入口是DeviceEntry函数,在这个函数里主要做的工作就是注册分发例程,调用KsInitializeDriver 函数,传入一个KSDEVICE_DESCRIPTOR类型的参数,该参数中指定了一个KSDEVICE_DISPATCH参数,用来注册各种例程。一个典型的KSDEVICE_DISPATCH参数如下所示:

extern
const
KSDEVICE_DISPATCH
DeviceDispatch 
=
{
    CDevice::Create,    
// Add
    CDevice::Start,     // Start
    NULL,               // PostStart
    NULL,               // QueryStop
    NULL,               // CancelStop
    NULL,               // Stop
    NULL,               // QueryRemove
    NULL,               // CancelRemove
    NULL,               // Remove
    NULL,               // QueryCapabilities
    NULL,               // SurpriseRemoval
    NULL,               // QueryPower
    NULL                // SetPower
}
;


这部分工作实际上是

 

 

CDevice::Create 例程中,我们要做的事情主要包括:Download firmware和生成必要Filter实例,如有必要还可以配置DMA等。Download firmware之前要先获得板子的product_id,然后调用Reset8051(0x09),通知下层开始传输fireware,传完后再调用Reset8051(0x08)通知下层传输完毕。生成Filter调用BdaCreateFilterFactory 函数,传入BDA_FILTER_TEMPLATE参数,在该参数中需要指定FilterDispatchFilterAutomationPinDescriptorsKSFILTER_CATEGORY(目录位置),NodeDescriptors CONNECTIONS FilterDispatch指定了Filter的各个分发例程,FilterAutomation用于提供PropertySet(属性集)和MethodSet(方法集),PinDescriptors用于指定Pin的各个分发例程,KSFILTER_CATEGORY指定生成的的Filter在哪个目录底下(硬件filter用目录索引的方法寻找实例,而不是像软件filter那样用GUID寻找实例),NodeDescriptors CONNECTIONS 共同指定了filter内部的功能拓扑图。

一个典型的FilterDispatch 如下图所示

  

const
KSFILTER_DISPATCH
FilterDispatch 
=
{
    CFilter::Create,        
// Create
    CFilter::FilterClose,   // Close
    NULL,                   // Process
    NULL                    // Reset
}
;

 

一个典型的PinDescriptors如下所示

AntennaPinDispatch 如下图所示

const
KSPIN_DESCRIPTOR_EX
InitialPinDescriptors[] 
=
{
    
//  Antenna Pin
    
//
    {
        
&AntennaPinDispatch, 分发例程
        
&AntennaAutomation,   // Pin上的属性集和方法集

//下面是该pin的类型定义
        {
            
0,  // Interfaces
            NULL,
            
0,  // Mediums
            NULL,
            SIZEOF_ARRAY(AntennaPinRanges),
            AntennaPinRanges,
            KSPIN_DATAFLOW_IN,
            KSPIN_COMMUNICATION_BOTH,
            NULL,   
// Name
            NULL,   // Category
            0
        }
,

下面指定传输类型和方式
        KSPIN_FLAG_DO_NOT_USE_STANDARD_TRANSPORT 
| 
        KSPIN_FLAG_FRAMES_NOT_REQUIRED_FOR_PROCESSING 
|  
        KSPIN_FLAG_FIXED_FORMAT,
        
1,      // InstancesPossible
        0,      // InstancesNecessary
        NULL,   // Allocator Framing
        NULL    // PinIntersectHandler
    }

}
;


一个典型的

  

const
KSPIN_DISPATCH
AntennaPinDispatch 
=
{
    CAntennaPin::PinCreate,         
// Create
    CAntennaPin::PinClose,          // Close
    NULL,                           // Process signal data
    NULL,                           // Reset
    NULL,                           // SetDataFormat
    CAntennaPin::PinSetDeviceState, // SetDeviceState
    NULL,                           // Connect
    NULL,                           // Disconnect
    NULL,                           // Clock
    NULL                            // Allocator
}
;

 

 

属性集和方法集可以在filter里提供,也可以在Node里提供。Filter上提供的属性集可以被应用层调用,而Node上提供的属性集则只能是BDA架构内的东西可以调用(如第一页所讲,tunner里的两个node都是Network Provider通过内置的GUID来访问的,node上只需要提供BDA中需要的属性集和方法集即可,比如设置频率等,而这些属性集方法集所绑定的GUID也是BDA内置的。)Filter上提供的属性集可以为上层定制很多特定功能,可以代替DeviceIoControl,实际上在BDA架构下建议使用DeviceIoControl

(据观察,我的程序中

Demodulator Node中有一个PropertySetKSPROPSETID_BdaAutodemodulate

Tunner Node中有 KSPROPERTY_BDA_RF_TUNER_FREQUENCY_MULTIPLIER

KSPROPERTY_BDA_RF_TUNER_FREQUENCY

KSPROPERTY_BDA_SIGNAL_STRENGTH

KSPROPERTY_BDA_SIGNAL_QUALITY

KSPROPERTY_BDA_SIGNAL_PRESENT

KSPROPERTY_BDA_SIGNAL_LOCKED

KSPROPERTY_BDA_SAMPLE_TIME

这几个属性,提供对内置frequencysignal的操作的实现。所有BDA内置的属性请参看DDK à Device Technology à Video Capture Device à Reference à Broadcast Driver Architecture Drivers à Broadcast Driver Architecture Property, Event, and Method Sets)。

视频采集驱动中最关键的操作:数据采集,发生在Capture FilterOutput Pin的状态从其他状态变成Start状态时。它会启动一个工作线程,不停的从底层采集数据存放在缓存中,并调用KsPinAttemptProcessing方法以响应Capture FilterProcess方法把数据往后传。

Stream PointersAVStream minidriver中把数据从一个filter传到下一个filter的方法,BDA扩展自AVStream,所以BDA传数据也用Stream PointersAVStream内部管理了一条数据队列,我们要做的事情是往队列里塞数据,把当前指针往后移以及销毁过期数据。Process操作会把Stream Pointer指向的数据复制给下一个与他相连的filter,具体细节被屏蔽,我们要关心的只有Stream Pointer。要把数据往后移,我们可以调用KsStreamPointerAdvance 函数 或者KsStreamPointerUnlock 函数(Eject 参数设置为TRUE),函数中传入要移动数据的Stream Pointer即可。调用完后需要再调用KsStreamPointerSetStatusCode 查看操作是否成功,如果有错误,则调用KsStreamPointerDelete 方法销毁数据(实际上不是真的销毁,只是减少引用计数。当引用计数减少到0的时候,数据才被真正销毁)。

Stream Pointers还提供了一套管理数据队列的方法,KsPinGetLeadingEdgeStreamPointer取得头指针,KsPinGetTrailingEdgeStreamPointer取得尾指针,KsPinGetFirstCloneStreamPointer取得当前正在用的数据的指针,KsStreamPointerGetNextClone则指向当前指针的下一个指针。

如果要传输的只是一个帧里的某一些数据,则调用KsStreamPointerAdvanceOffsets 或者 KsStreamPointerAdvanceOffsetsAndUnlock.函数。

 

DeviceAdd例程中,我们还可以在KsDeviceàContext中加入我们需要的数据,这批数据的生命周期就会一直延续到DeviceRemove例程完成为止,在程序中定义一些全局变量是不可取的,最好全放在KsDeviceàContext中。

BDA驱动学习笔记(3):NODE例程

 

定义一个Filter的分发例程时,如有需要,可以指定DEFINE_KSFILTER_NODE_DESCRIPTORS以便在Filter里生成Node

一个典型的NODE_DESCRIPTORS如下所示

 

const  KSNODE_DESCRIPTOR DgtlTunerFilterNodeDescriptors[]  =

{

       
{

       
&RFTunerNodeAutomation,      // PKSAUTOMATION_TABLE AutomationTable;

       
&KSNODE_BDA_RF_TUNER,   // Type

       
&guidTunerName               // Name

       }
,

       
{

       
&COFDMDemodulatorNodeAutomation, // PKSAUTOMATION_TABLE AutomationTable;

       
&KSNODE_BDA_COFDM_DEMODULATOR, // Type

       
&guidDemodName                  // Name

       }


}
;

 

这里定义了两个Node,每个Node都指定了Automation TableNode Type Name

一个典型的Automation Tale如下所示

DEFINE_KSAUTOMATION_TABLE(RFTunerNodeAutomation)

{

       DEFINE_KSAUTOMATION_PROPERTIES(RFTunerNodeProperties),

       DEFINE_KSAUTOMATION_METHODS_NULL,

       DEFINE_KSAUTOMATION_EVENTS_NULL

}
;

 


这里只定义了属性集,方法集和事件集都被置为空。进一步的,

RFTunerNodeProperties定义如下

 

DEFINE_KSPROPERTY_SET_TABLE(RFTunerNodeProperties)

{

       DEFINE_KSPROPERTY_SET

       (

              
&KSPROPSETID_BdaFrequencyFilter,                // Set

              SIZEOF_ARRAY(RFTunerBdaFrequencyFilter),        
// PropertiesCount

              RFTunerBdaFrequencyFilter,                      
// PropertyItems

              
0,                                              // FastIoCount

              NULL                                            
// FastIoTable

       ),

       DEFINE_KSPROPERTY_SET

       (

              
&KSPROPSETID_BdaSignalStats,                    // Set

              SIZEOF_ARRAY(RFTunerBdaSignalStats),            
// PropertiesCount

              RFTunerBdaSignalStats,                          
// PropertyItems

              
0,                                              // FastIoCount

              NULL                                            
// FastIoTable

       )

}
;

 

可以看到,Tunner Node里扩展了KSPROPSETID_BdaFrequencyFilteKSPROPSETID_BdaSignalStats 两个属性集。更进一步的,

RFTunerBdaFrequencyFilter的定义如下

 



DEFINE_KSPROPERTY_TABLE(RFTunerBdaFrequencyFilter)

{

       DEFINE_KSPROPERTY_ITEM

       (

              KSPROPERTY_BDA_RF_TUNER_FREQUENCY_MULTIPLIER,   
// 1

              RFTunerNodeGetFrequencyMultiplier,              
// GetSupported or Handler

              
sizeof(KSP_NODE),                               // MinProperty

              
sizeof(DWORD),                                  // MinData

              RFTunerNodeSetFrequencyMultiplier,              
// SetSupported or Handler

              NULL,                                           
// Values

              
0,                                              // RelationsCount

              NULL,                                           
// Relations

              NULL,                                           
// SupportHandler

              
0                                               // SerializedSize

       ),

       DEFINE_KSPROPERTY_ITEM

       (

              KSPROPERTY_BDA_RF_TUNER_FREQUENCY,              
// 1

              RFTunerNodeGetFrequency,                        
// GetSupported or Handler

              
sizeof(KSP_NODE),                               // MinProperty

              
sizeof(DWORD),                                  // MinData

              RFTunerNodeSetFrequency,                        
// SetSupported or Handler

              NULL,                                           
// Values

              
0,                                              // RelationsCount

              NULL,                                           
// Relations

              NULL,                                          
// SupportHandler

              
0                                               // SerializedSize

       )

}
;

 

该属性集中的KSPROPERTY_BDA_RF_TUNER_FREQUENCY_MULTIPLIERKSPROPERTY_BDA_RF_TUNER_FREQUENCY属性被扩展。

 

BDA架构目前提供的Node Type有如下几个

KSNODE_BDA_RF_TUNER

KSNODE_BDA_QAM_DEMODULATOR

KSNODE_BDA_QPSK_DEMODULATOR

KSNODE_BDA_8VSB_DEMODULATOR

KSNODE_BDA_COFDM_DEMODULATOR

KSNODE_BDA_OPENCABLE_POD

KSNODE_BDA_COMMON_CA_POD

KSNODE_BDA_PID_FILTER

KSNODE_BDA_IP_SINK

 

Node Name由自己指定,只是不能有重复。

 

指定完Node后,还要指定它们的连接方式。

一个典型的连接定义如下

filter出来,连到第0node的第0pin;第二条从第0Node的第1pin出来,连到第1Node的第0pin;第三条从第1Node的第1pin出来,连后后一个filter

const
KSTOPOLOGY_CONNECTION TemplateTunerConnections[] 
=
{
    
{-1,  0,  0,  0}// from upstream filter to 0 pin of 0 node
    {0,  1,  1,  0}// from 1 pin of 0 node to 0 pin of 1 node
    {1,  1,  -11}// from 1 pin of 1 node to downstream filter
}
;


这张表指定了三条连接,如注释中所讲,第一条从前一个

指定完连接线路后,还要指定一个Node间的连接点。

一个典型的AntennaTransportJoints如下

Node间的连接点是上面那张链接表中的第二条线路。连接点在指定Node的控制Pin时有用。为什么要指定控制Pin?因为Node从外部是没法访问的,想要访问Node,必须现访问该Node的控制Pin,然后让控制Pin去访问Node。如下图所示

const
ULONG   AntennaTransportJoints[] 
=
{
 
1 // Second element in the preceding KSTOPOLOGY_CONNECTION array.
}
;

 


表明

 

转载BDA驱动学习笔记(1--3)_第2张图片
Node1
处在Pin1Joint1之间,所以Node1的控制Pin就是Pin1;而Node2处在Joint1Pin2之间,所以Node2的控制PinPin2。大多数时候一张拓扑图中只有一个连接点。可以通过BdaPropertyGetControllingPinId 函数取得控制PinID

 

 

BDA架构允许一个驱动运行很多的实例,但是一个时间里只能有一个实例的状态处于运行状态,因为硬件资源往往只有一个。为此需要提供同步机制,BDA中用方法集来保持同步。一个典型的同步方法集如下所示:

 

DEFINE_KSMETHOD_TABLE(BdaChangeSyncMethods)
{
    DEFINE_KSMETHOD_ITEM_BDA_START_CHANGES(
        CFilter::StartChanges,
        NULL
        ),
    DEFINE_KSMETHOD_ITEM_BDA_CHECK_CHANGES(
        CFilter::CheckChanges,
        NULL
        ),
    DEFINE_KSMETHOD_ITEM_BDA_COMMIT_CHANGES(
        CFilter::CommitChanges,
        NULL
        ),
    DEFINE_KSMETHOD_ITEM_BDA_GET_CHANGE_STATE(
        CFilter::GetChangeState,
        NULL
        )
}
;

 

应用程序引发上述例程,例程实现中通知驱动做相应的操作。

 CFilter::StartChanges中,调用BdaStartChanges 方法通知驱动开始变更。

 CFilter::CheckChanges中,调用BdaCheckChanges方法通知驱动检查变更。

 CFilter::CommitChanges中,调用BdaCommitChanges方法通知驱动提交变更。

 CFilter::GetChangeState中,调用BdaGetChangeState方法获取当前状态。

 

 

最后交代一个原则:每次当driverfirmware,或者硬件发生改变时,都调用BdaFilterFactoryUpdateCacheData 更新DirectShow中相应的数据。

 

至此,BDA驱动的大体架构已出。

AVStream minidriver要做的,考虑到bda是在AVStream基础上的扩展,我们写bda minidriver时也要做相同的工作。类似的工作在WDM驱动里也要做,不同的是WDM的例程注册都是类似赋值的语句,而AVStream minidriver的例程注册使用一些模板完成,为我们省掉很多工作。

你可能感兴趣的:(转载BDA驱动学习笔记(1--3))