Case Study: Extending Frontline ATT Decoder for a Fixed Configuration Service
Background
Normally, services in ATT are discovered so that the configuration of the database can be as flexible as possible. In the ATT world, there are rules but freedom generally reigns supreme. The price we pay for this flexibility is that the database must be discovered.
Consider the following database: Normally, it would take a number of transactions between devices to establish these three entities because the Characteristic doesn’t have to be at handle 11
Handle | Contents |
---|---|
10 | Service UUID |
11 | Characteristic UUID |
12 | Value UUID |
The database could look like this:
Handle | Contents |
---|---|
65 | Service UUID |
111 | Characteristic UUID |
212 | Value UUID |
But what if, when we designed the database, we also specify the handles? We would declare the Service UUID to be the ‘base handle’. Then all other handles would be offset from that base handle.
Handle | Contents |
---|---|
<base> | Service UUID |
<base +1> | Characteristic UUID |
<base +2> | Value UUID |
Now there is no need for discovery at all, other than to determine the base handle. Once that is known the rest of the handles are also known. Even discovering the service can be avoided by using the ADV_IND PDU. The specification allows for ‘additional data’. This additional data could contain the base handle.
Problem:
This minimalist approach means that the very first ATT frame in a conversation could be the exchange of meaningful information instead of some form of discovery. It also means that the Frontline sniffer is deprived of essential information for decoding that meaningful information. The Frontline decoders must be told how to decode the characteristics or all you will get is raw hex data.
Solution:
The Frontline architecture allows for extensions by customers without actually modifying the Frontline decoders.
In this exercise, we will be doing the following:
- We insert code into the Frontline le_adv decoder to detect an advertisement of your service. We will extract the base handle from the ‘additional data’ field of the ADV_IND PDU.
- We insert code into the Frontline le_adv decoder to detect a CONN_REQ PDU that signals the beginning of a conversation. If we have the base handle, we will manually insert UUID/handling mappings for use by the ATT layer
- We will insert code into the Frontline ATT decoder to detect and decode your service characteristics.
Procedure
Note: This procedure is fairly involved. Feel free to consult with Frontline Tech Support for assistance.
-
Find the ‘\Frontline Test Equipment\My Decoders’ directory and create a file called ‘Custom Attributes ATT.dh’. Copy into that file thefollowing code. Here we have added two new UUIDs to the Frontline ATT decoder. The service UUID is informational only and will never reference any data to be decoded. The Characteristic UUID contains additional fields and contains a call to a DecoderScript GROUP that will decode that characteristic.
Custom Attributes ATT.dh
Table tiUUIDs APPENDS_ONLY GATT_UUIDS
{ 0x7ffffe00 "My Service" "My Service" }
{ 0x7ffffe02 "My Data" "My Data" 0 gMyData }
ENDTABLE
GROUP gMyData
{
FIELD myDataField (toEndOfLayer) (stringofhex) “my data”
}
-
Create a file in ‘\Frontline Test Equipment\My Decoders’ called ‘Custom Attributes LE_ADV.dh’. Copy into that file the following code. Here we are decoding the ‘additional data’ in ADV_IND PDUs. Notice that we are also appending table GATT_UUIDS in le_ADV. We do this so that the service UUID will be properly decoded in both decoders. It just so happens that the tables in both decoders have the same name.
Custom Attributes LE_ADV.dh
Table tiUUIDs APPENDS_ONLY GATT_UUIDS
{ 0x7ffffe00 "My Service" "My Service" }
ENDTABLE
Table tMyServiceDataTypes APPENDS_ONLY tServiceDataTypes
{ 0xabcd "MySvc” "My Service" 0 gmyServiceADData } ENDTABLE
GROUP gmyServiceADData
{
FIELD BaseHandle (fixed 1) RETRIEVE (StorePersistentField "MyServiceBaseHandle") (decimal) "Base Handle"
// any more additional data decoding you might need.
}
-
Create a file in ‘\Frontline Test Equipment\My Decoders’ END MAIN PATH LE_ADV.dh’. Copy into that file the following code. Here we are checking the current frame to see if we should build our database. We can’t do it in the ADV_IND because the Access_Address might not be right. We need a CONNECT_REQ for what we want to do. So we check to see if we have had an ADV_IND with the service we want and if this frame is a CONN_REQ.
END MAIN PATH LE_ADV.dh
// IF WE HAVE SEEN AN ADV_IND WITH THE BASE uuid, WE ARE GOOD TO GO....MAYBE
GROUP gCheckBuildDB if(persistentstaticexists " MyServiceBaseHandle ")
{
GROUP gDoBuildDB if(fieldis equalto 0x05 adv_type) // if this is a CONNECT_REQ
{
GROUP gBuildMyDataBase; // Go build our database. The code for that is in Custom Attributes LE_ADV.dh
}
}
-
Back to ‘Custom Attributes LE_ADV.dh’ from step 2. Copy into the end of that file the following code. Here is where we tell the ATT decoder about the mappings between handles and UUIDs. There are some supporting values created by gSetupDBID . Then it is all just repeating the same three lines of decoderscript over and over. Notice that the handle value is relative to the base handle so handle 1 (using our example) would be stored as handle 11.
Add to: Custom Attributes LE_ADV.dh
GROUP gBuildMyDataBase
{
GROUP gSetupDBID;
// These next three lines get repeated for every UUID/handle mapping
// you want to make. Just change the storeinteger calls with the
// numbers you want. For each UUID/handle pair, call gStoreUUID;
FIELD SetOffset_1 (fixed 0) RETRIEVE(storeinteger 1) (hex) SUPPRESS_DETAIL Store chHandle
FIELD SetUUID_1 (fixed 0) RETRIEVE(storeinteger 0x2803) (hex) SUPPRESS_DETAIL Store ShortUUID
GROUP gStoreUUID;
}
GROUP gSetupDBID
{
FIELD GetSide (fixed 0) RETRIEVE(intraframefield "side") (hex) SUPPRESS_DETAIL store side
FIELD MyBaseHandle (fixed 0) RETRIEVE(persistentfield " MyServiceBaseHandle") (hex) SUPPRESS_DETAIL
// Create DBID which is Access Address + Role
FIELD sideIs0 (Fixed 0) if(fieldis equalto 0 side) RETRIEVE (storeinteger 1) (decimal) SUPPRESS_DETAIL Store Role
FIELD sideIs1 (Fixed 0) if(fieldis equalto 1 side) RETRIEVE (storeinteger 0) (decimal) SUPPRESS_DETAIL Store Role
FIELD LE_RequesterID (fixed 0) Retrieve(StoreField AccessAddress) also (shiftleft 8) also (addfield role) also (shiftleft 16) (hex) DBID Store DBID
}
GROUP gStoreUUID
{
FIELD MyAbsoluteHandle (fixed 0) RETRIEVE(storefield chHandle) also (addfield MyBaseHandle) (hex) SUPPRESS_DETAIL
FIELD gUniqueHandle (fixed 0) RETRIEVE(StoreField DBID) also (AddField MyAbsoluteHandle) (hex) SUPPRESS_DETAIL
FIELD chUUIDShrtStore (fixed 0) PROCESSING(ATTSetUUIDMapping ATTMappings gUniqueHandle ShortUUID ) (Hex) SUPPRESS_DETAIL // Store mapping
}