WHAD Device discovery

When a host computer starts communicating with a WHAD interface, it sends a series of message to discover its characteristics and its supported domains in order to tailor the host features and match the capabilities exposed by the interface.

Discovery of interface characteristics

First, the host sends a DeviceInfoQuery message to the WHAD interface that is expected to responds with a DeviceInfoResp message. This message must provide the following information:

  • the device type

  • the device unique ID

  • the minimum supported version of WHAD protocol

  • the device maximum communication speed

  • the author name (limited to 64 characters)

  • the firmware project URL (linited to 256 characters)

  • the firmware major version number

  • the firmware minor version number

  • the firmware revision number

  • a list of capabilities

To build such a message, we need to define first our capabilities:

const whad_domain_desc_t CAPABILITIES[] = {
    {
        /* We support the Bluetooth Low Energy wireless protocol. */
        DOMAIN_BTLE,

        /* For this protocol, we are able to sniff, inject and emulate
           central and peripheral roles. */
        (whad_capability_t)(CAP_SNIFF | CAP_INJECT | CAP_SIMULATE_ROLE),

        /* We define the list of supported commands, based on NanoPb command values. */
        (
        CMD(ble_BleCommand_SniffAdv) |
        CMD(ble_BleCommand_SniffConnReq) |
        CMD(ble_BleCommand_SniffActiveConn) |
        CMD(ble_BleCommand_Start) |
        CMD(ble_BleCommand_Stop) |
        CMD(ble_BleCommand_SendRawPDU) |
        CMD(ble_BleCommand_SendPDU) |
        CMD(ble_BleCommand_CentralMode) |
        CMD(ble_BleCommand_PeripheralMode) |
        CMD(ble_BleCommand_ConnectTo) |
        CMD(ble_BleCommand_Disconnect) |
        CMD(ble_BleCommand_PrepareSequence) |
        CMD(ble_BleCommand_TriggerSequence) |
        CMD(ble_BleCommand_DeleteSequence) |
        CMD(ble_BleCommand_ScanMode)

        )
    },

    /* End of supported domains, MUST be there to mark the end of this list. */
    {DOMAIN_NONE, CAP_NONE, 0x00000000}
};

Once our capabilities defined, we can build the response message:

using namespace whad::discovery;

DeviceInfoResp(
    Devices::Butterfly,

    (uint8_t *)"MyAwesomeDevice",

    /* Supports WHAD protocol v2 */
    2,

    /* Max speed for our UART hw */
    115200,

    /* Firmware author. */
    "John Doe <jdoe@example.org",

    /* Firmware project URL. */
    "https://github.com/whad-team/demofw",

    /* Firmware version: major, minor, rev. */
    1, 0, 0,

    /* Capabilities as defined above. */
    CAPABILITIES
);

Note

The CAPABILITIES structure created above is used by both whad::discovery::DeviceInfoResp and whad::discovery::DomainInfoResp to provide the host with the supported domains and the related interface capabilities for the first one, and the supported commands for a specific domain for the second one.

Discovery of supported commands for a given domain

Once the host has discovered the device’ supported domains and its capabilities, it queries the WHAD interface to retrieve the implemented commands for each supported domain by sending multiple DeviceDomainInfoQuery messages.

Each of these messages must be answered by the WHAD interface with a DeviceDomainInfoResp specifying the supported commands for the requested domain. This message can be created through the whad::discovery::DomainInfoResp class, as shown below:

void discovery_handler(NanoPbMsg message)
{
    NanoPbMsg *response = NULL;

    /* ... */

    /* Wrap our NanoPbMsg into a GenericMsg instance. */
    whad::discovery::DiscoveryMsg disc_msg(p_message);

    switch (disc_msg.getType())
    {
        /* DeviceDomainInfoQuery message. */
        case whad::discovery::DomainInfoQueryMsg:
        {
            /* Parse our domain info query. */
            whad::discovery::DomainInfoQuery domain_info_query()

            if (domain_info_query.getDomain() == whad::discovery::Domains::DomainBtLE)
            {
                response = new whad::discovery::DomainInfoResp(
                    domain_info_query.getDomain(),
                    CAPABILITIES
                );
            }
            else
            {
                /* Send an error if domain is not supported. */
                response = new whad::generic::Error();
            }
        }
        break;

        /* ... */
    }

}

Note

whad::discovery::DomainInfoResp will pick the corresponding supported commands from the provided capabilities array and build a valid message from it.

Transport speed update

The WHAD interface, in its DeviceInfoResp message, provides the host with its maximum communication speed and the host can at anytime decide to switch to that speed to get faster communication. This speed change is requested by the host through a SetTransportSpeed message. This message must be acknowledged by the WHAD interface before setting up the hardware to use this new speed setting.

First, the WHAD interface needs to create and send a generic CommandResult message with a result code of WHAD_RESULT_SUCCESS and then setup the new communication speed. Once the new speed configured and in use, the WHAD interface must send a new DeviceReadyResp message to let the host know that it is ready to operate.

NanoPbMsg response = NULL;


/* Send a success result code. */
response = new whad::generic::Success();
whad::send(*response);
delete response;

/* Re-configure communication hardware with new speed. */

/* Once done and ready, send a DeviceReadyResp to host. */
response = new whad::discovery::ReadyResp();
whad::send(*response);
delete response;

Discovery message processing template

The following code is a basic code template for processing discovery messages:

void discovery_handler(NanoPbMsg message)
{
    NanoPbMsg *response = NULL;

    /* Parse the incoming message as a discovery message. */
    whad::discovery::DiscoveryMsg disc_msg(message);

    /* Process any discovery message. */
    switch(disc_msg.getType())
    {

        /* DeviceInfoQuery message. */
        case whad::discovery::MessageType::DeviceInfoQueryMsg:
        {
            /* Parse message as device info query. */
            whad::discovery::DeviceInfoQuery query(message);

            /* Make sure we support the provided protocol version. */
            if (query.getVersion() >= 2)
            {
                /* Fill response message with DeviceInfoResp. */
                response = new whad::discovery::DeviceInfoResp(
                    query.getDomain(),
                    CAPABILITIES
                )
            }
            else
            {
                /* Client protocol version is too old. */
                response = new whad::generic::Error();
            }

        }
        break;

        /* DeviceDomainInfoQuery message. */
        case whad::discovery::MessageType::DomainInfoQueryMsg:
        {
            /* Parse incoming message. */
            whad::discovery::DomainInfoQuery query(message);

            response = new whad::discovery::DomainInfoResp(
                query.getDomain(),
                CAPABILITIES
            );
        }
        break;

        /* SetTransportSpeed */
        case whad::discovery::MessageType::SetSpeedMsg:
        {
            /* Send a success result code. */
            response = new whad::generic::Success();
            whad::send(*response);
            delete response;

            /*"
             * Re-configure communication hardware with new speed:
             *
             * 1. Flush current UART buffer
             * 2. Re-configure UART interface
             **/

            /* Once done and ready, send a DeviceReadyResp to host. */
            response = new whad::discovery::ReadyResp();
        }
        break;

        /* DeviceResetQuery */
        case whad::discovery::MessageType::DeviceResetMsg:
        {
            /* Reset WHAD interface state. */
            interface_reset_state();

            /* Send a DeviceReadyResp. */
            response = new whad::discovery::ReadyResp();
        }
        break;

        default:
        {
            /* Unsupported command. */
            response = whad::generic::Error();
        }
        break;
    }

    /* Send response. */
    whad::send(*response);
    delete response;
}

Discovery API reference

namespace discovery

Enums

enum MessageType

Discovery message type

Values:

enumerator UnknownMsg

Unknown message type

enumerator DeviceInfoQueryMsg

Device info query

enumerator DeviceInfoRespMsg

Device info response

enumerator DeviceResetMsg

Device reset query

enumerator ReadyRespMsg

Device ready notification

enumerator DomainInfoQueryMsg

Device domain info query

enumerator DomainInfoRespMsg

Device domain info response

enumerator SetSpeedMsg

Set device speed query

enum Devices

Values:

enumerator Esp32BleFuzzer

Espressif ESP32 BLE fuzzer

enumerator Butterfly

nRF52 running ButteRFly firmware.

enumerator Btlejack

Btlejack (nRF52-based Micro:Bit or DK)

enumerator VirtualDevice

Virtual device

enum Domains

Available domains.

Values:

enumerator DomainNone

None

enumerator DomainPhy

PHY domain

enumerator DomainBtClassic

Bluetooth Classic

enumerator DomainBtLE

Bluetooth Low Energy

enumerator DomainDot15d4

IEEE 802.15.4

enumerator DomainSixLowPan

6LoWPAN

enumerator DomainEsb

Nordic Semiconductor Enhanced ShockBurst

enumerator DomainLogitechUnifying

Logitech Unifying

enumerator DomainMosart

Mosart

enumerator DomainAnt

Nordic Semiconductor ANT

enumerator DomainAntPlus

Nordic Semiconductor ANT+

enumerator DomainAntFs

Nordic Semiconductor ANT-FS

Functions

bool isDomainSupported(const whad_domain_desc_t *capabilities, Domains domain)
class DiscoveryMsg : public whad::NanoPbMsg
#include <base.hpp>

Subclassed by whad::discovery::DeviceInfoQuery, whad::discovery::DeviceInfoResp, whad::discovery::DeviceReset, whad::discovery::DomainInfoQuery, whad::discovery::DomainInfoResp, whad::discovery::ReadyResp, whad::discovery::SetTransportSpeed

Public Functions

DiscoveryMsg()

Discovery message base class.

DiscoveryMsg(NanoPbMsg &pMessage)

Discovery message base class.

Parameters:

pMessage[in] NanoPbMsg object containing a discovery domain message

~DiscoveryMsg()

Discovery message base class destructor.

MessageType getType(void)

Identify the underlying discovery message.

This method can be used when parsing incoming Discovery domain message to identify the type of message it contains and process it later.

Returns:

Discovery message type.

class DeviceInfoQuery : public whad::discovery::DiscoveryMsg
#include <devinfo.hpp>

Public Functions

DeviceInfoQuery(DiscoveryMsg &message)

Parse a DiscoveryMsg object as a DeviceInfoQuery and extract its parameters.

DeviceInfoQuery(uint32_t protoVersion)

Create a DeviceInfoQuery message with a specific protocol version.

Parameters:

protoVersion – Version of the WHAD protocol supported by this device

uint32_t getVersion()

Return the current protocol version.

Return values:

Protocol – version

class DeviceInfoResp : public whad::discovery::DiscoveryMsg
#include <devinfo.hpp>

Public Functions

DeviceInfoResp(Devices deviceType, uint8_t deviceId[16], uint32_t protoMinVer, uint32_t maxSpeed, std::string sFirmwareAuthor, std::string sFirmwareUrl, uint32_t fwVersionMajor, uint32_t fwVersionMinor, uint32_t fwVersionRevision, whad_domain_desc_t *capabilities)

Build a device information response.

This message must be sent in response to a Device Information request sent by the host to give all the details about the device and its firmware.

Parameters:
  • deviceType – Specifies the device type

  • deviceId – An ID that uniquely identifies this device\

  • protoMinVer – Minimal version of WHAD protocol supported by this device

  • maxSpeed – Maximum communication speed supported by the device

  • sFirmwareAuthor – Firmware’s author name

  • sFirmwareUrl – Firmware source code or project URL

  • fwVersionMajor – Major version of the firmware running on the device

  • fwVersionMinor – Minor version of the firmware running on the device

  • fwVersionRevision – Minor version of the firmware running on the device

  • capabilities – Specifies the device capabilities and supported commands per domain

class DeviceReset : public whad::discovery::DiscoveryMsg
#include <devreset.hpp>

Public Functions

DeviceReset()

Build a device reset message.

DeviceReset(NanoPbMsg &message)

Build a device reset message from a NanoPb message.

Parameters:

message[in] message to wrap as a DeviceReset message

class DomainInfoQuery : public whad::discovery::DiscoveryMsg
#include <domaininfo.hpp>

Public Functions

DomainInfoQuery(DiscoveryMsg &message)

Parse a DomainInfoQuery from a DiscoveryMsg.

Parameters:

message – Original DiscoveryMsg to parse as a DomainInfoQuery

DomainInfoQuery(Domains domain)

Create a DomainInfoQuery with a specific domain.

Domain information query

Parameters:

domain – Domain to query

Domains getDomain()

Retrieve the current domain.

class DomainInfoResp : public whad::discovery::DiscoveryMsg
#include <domaininfo.hpp>

Public Functions

DomainInfoResp(Domains domain, whad_domain_desc_t *capabilities)

Build a domain information response.

Domain information response

TODO: try to expose a vector of capabilities (or hashmap ?)

This message must be sent in response to a Domain Information request sent by the host to provide the supported capabilities.

Parameters:
  • domain – The target domain queried by the host

  • capabilities – The device’s capabilities structure

class ReadyResp : public whad::discovery::DiscoveryMsg
#include <ready.hpp>

Public Functions

ReadyResp()

Build a discovery ready response message.

This message must be sent in response to a Reset query from the host, to acknowledge the fact the device has been reset and is ready to process incoming commands.

class SetTransportSpeed : public whad::discovery::DiscoveryMsg
#include <speed.hpp>

Public Functions

SetTransportSpeed(DiscoveryMsg &pMessage)

Create a SetTransportSpeed message from a discovery message.

Parameters:

message[in] Discovery message to parse as a SetTransportSpeed message.

SetTransportSpeed(uint32_t speed)

Create a SetTransportSpeed message from a discovery message.

Parameters:

speed[in] Transport speed to use (in bauds)

uint32_t getSpeed()

Get transport speed.

Return values:

Transport – speed in bauds.