C

Porting DeviceLink Communication

Purpose

A device link component enables communication between host and device. It allows them to exchange touch, screenshot, performance, or log data, for debugging or testing purposes.

The custom device link implementation provides the interface between the protocol facilities and the hardware. The two following functions must be implemented:

The following is an example implementation for the functions mentioned above:

struct ExampleDeviceLinkInterface : Qul::Platform::DeviceLinkInterface
{
    void platformInit()
    {
        // Enable and start actual transfer and reception of the serial port,
        // eg. enabling interrupts
#if QUL_PLATFORM_DEVICELINK_CHANNEL == CHANNEL_USART3
        //HAL_NVIC_SetPriority(USART3_IRQn, 0, 1);
        //HAL_NVIC_EnableIRQ(USART3_IRQn);
#else
#error Unknown QUL_PLATFORM_DEVICELINK_CHANNEL
#endif
    }

    void transmitChars(const uint8_t *data, uint32_t size)
    {
        QUL_UNUSED(data);
        QUL_UNUSED(size);

        // Use the serial port to transfer the data to the host
        // HAL_UART_Transmit(&uartHandle, (uint8_t *) data, size, 0xFFFF);
    }

    Qul::Platform::FramebufferFormat framebufferFormat(
        const Qul::PlatformInterface::LayerEngine::ItemLayer *layer) override
    {
        // Layer engine enabled?
        if (layer)
            return Qul::Platform::ExampleLayerEngine::framebufferFormat(layer);

        // Assuming we use 32bpp framebuffers
        static const int BytesPerPixel = 4;

        static const int ScreenWidth = QUL_PLATFORM_DEFAULT_SCREEN_WIDTH;
        static const int ScreenHeight = QUL_PLATFORM_DEFAULT_SCREEN_HEIGHT;

        Qul::Platform::FramebufferFormat format;

        format.address = frontBuffer();
        format.width = ScreenWidth;
        format.height = ScreenHeight;
        format.bytesPerLine = ScreenWidth * BytesPerPixel;
        format.bitsPerPixel = BytesPerPixel * 4;
        format.redChannel = {16, 8};
        format.greenChannel = {8, 8};
        format.blueChannel = {0, 8};
        format.swapBytes = 0;
        return format;
    }
};

If layer support is enabled, the example implementation of Qul::Platform::DeviceLinkInterface::framebufferFormat() forwards to ExampleLayerEngine:

Qul::Platform::FramebufferFormat ExampleLayerEngine::framebufferFormat(
    const PlatformInterface::LayerEngine::ItemLayer *layer)
{
    auto base = static_cast<const ExampleItemLayerBase *>(layer);

    Qul::Platform::FramebufferFormat format;

    format.address = base->drawingDevice.bits();
    format.width = base->drawingDevice.width();
    format.height = base->drawingDevice.height();
    format.bytesPerLine = base->drawingDevice.bytesPerLine();
    format.bitsPerPixel = base->drawingDevice.bitsPerPixel();

    switch (format.bitsPerPixel) {
    case 16:
        format.redChannel = {0, 5};
        format.greenChannel = {5, 6};
        format.blueChannel = {11, 5};
        format.swapBytes = 2;
        break;
    case 32:
        format.redChannel = {16, 8};
        format.greenChannel = {8, 8};
        format.blueChannel = {0, 8};
        format.swapBytes = 0;
        break;
    default:
        QUL_ASSERT(false, QulError_LayerEngine_UnsupportedColorDepth, format.bitsPerPixel);
    }

    return format;
}

The example implementation also defines another function, which returns the device link interface for your device.

DeviceLinkInterface *getDeviceLinkInterface()
{
    static ExampleDeviceLinkInterface deviceLink;
    return &deviceLink;
}

See also QUL_PLATFORM_DEVICELINK_ENABLED and QUL_PLATFORM_DEVICELINK_CHANNEL.

Hardware setup

After initializing hardware of the desired channel, which is usually a serial port, check if the device link is enabled and initialize it.

if (Qul::Platform::DeviceLink::instance())
    Qul::Platform::DeviceLink::instance()->init();

In your serial receiver function, transfer the received data to the device link component for decoding.

void UART_ReceiveInterrupt()
{
    Qul::PlatformInterface::deviceLinkBytesReceived(&receivedByte, 1);
}

Reimplement the low-level write functions to direct their content through the device link protocol, avoiding interference with the serial port protocol.

On most platforms the low-level write functions that need to be reimplemented are:

void putChar(char &character);
int _write(int file, char *ptr, int len);

The following example implementation should be adapted for each of these.

auto deviceLink = Qul::Platform::DeviceLink::instance();
if (deviceLink)
    deviceLink->printMessage(message, len);
else
    sendDataDirectlyToSerialPort();

Updating the logging implementation

Modify the logFlush function implementation that you saw earlier, to write directly to the serial port using the printMessage function of the device link component.

Requirements for rebuilding the platform

Install the protobuf and grpcio-tools Python packages to rebuild the platform with device link enabled.

Available under certain Qt licenses.
Find out more.