This section describes the library's data processing sequence. Whenever a new data packet is captured by the USBMC, the following occurs:
Library calls the INativeListener.ProcessRAWBuffer method of each registered native listener. The full data buffer is passed to the listener. A data buffer may contain several URB packets. It is listener's responsibility to separate and process them. If the listener sets the bStopParsing parameter to TRUE, no further processing is performed on this data buffer by the library.
If none of native listeners deny the further processing, raw data is parsed. After that, for each URB
in the buffer, USBMC calls INativeListener.OnPacketDown or INativeListener.OnPacketUp (_IMonitoringEvents.OnPacketDown or _IMonitoringEvents.OnPacketUp). Native listener can set the bStopParsing
parameter to TRUE
to stop further processing of each individual URB
.
USBMC parses the packet and calls the appropriate methods of the INativeListener or _IMonitoringEvents interfaces, depending on the type of the packet.
The following is a pseudo-code of library's data processing pipeline:
// pData is a pointer to data chunk, received from driver
void ProcessBuffer(void* pData, DWORD dwDataSize)
{
// for each client
for(...)
{
// this step is for NATIVE clients only
pClient->ProcessRawBuffer(...);
}
for (USBPACKET* packet=(USBPACKET *) pData, *stop=(USBPACKET*) ((BYTE *) pData + dwSize);
packet < stop && packet->Size;
(BYTE*&) packet+=packet->Size)
{
// for each client
for(...)
{
if(packet->Flags & UPF_DOWN)
pClient->OnPacketDown(...);
else
pClient->OnPacketUp(...);
switch(packet->EventType)
{
case EVENT_URB:
{
// parsing urb here
USBPACKET_URB* pUrb = (USBPACKET_URB *)pPacket;
BYTE* pUrbData = ...;
DWORD dwUrbSize = ...;
pClient->OnUrb(pUrbData,dwUrbSize);
switch (pUrb->urb.UrbHeader.Function)
{
case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
pClient->OnGetDescriptorFromDevice();
break;
// ...
}
break;
}
case EVENT_DEVICECONNECTED:
pClient->OnConnection(...);
break;
// ...
}
}
}
};
Let's take a [OnGetDescriptorFromInterface] method as an example.
USBMC uses URB_CONTROL_DESCRIPTOR_REQUEST
structure to get the data from a URB packet. Native listener has the following signature for OnGetDescriptorFromInterface
method:
HRESULT OnGetDescriptorFromInterface (FILETIME *fTime, void* pData, ULONG Size,
BYTE Index, BYTE DescriptorType, USHORT LanguageId);
pData
points to the packet that has a type USBPACKET
and can be cast to USBPACKET_URB
. So, native client can manually parse this packet and extract fields it needs. Besides USBMC automatically decodes and provides several packet fields, such as Index
, DescriptorType
and LanguageId
.
Manual parsing can be implemented in C++ in this way:
USBPACKET_URB* pUrb = (USBPACKET_URB*) pData;
_URB_CONTROL_DESCRIPTOR_REQUEST &r = pUrb->urb.UrbControlDescriptorRequest;
// use r variable here
```CPP
Please note that managed (dispatched) interface has a different method signature, without `pData` and `Size`:
```CPP
OnGetDescriptorFromInterface([in] DATE time,
[in] BYTE Index, [in] BYTE DescriptorType, [in] USHORT LanguageId);
Nevertheless, several managed callback methods (such as _IMonitoringEvents.OnPacketDown, _IMonitoringEvents.OnPacketUp and _IMonitoringEvents.OnUrb) receive raw data by means of a safe array, allowing them to manually parse data.