C

Porting DeviceLink Communication

The 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 Platform::DeviceLinkInterface provides the interface between the protocol facilities and the hardware. The 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
    }

    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)
        QUL_DECL_OVERRIDE
    {
        // 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.offset = 16;
        format.redChannel.length = 8;
        format.greenChannel.offset = 8;
        format.greenChannel.length = 8;
        format.blueChannel.offset = 0;
        format.blueChannel.length = 8;
        format.swapBytes = 0;
        return format;
    }
};

In case the ported platform has a layer engine, the framebufferFormat function needs to be implemented differently to account for this:

Qul::Platform::FramebufferFormat framebufferFormat(const PlatformInterface::LayerEngine::ItemLayer *layer)
{
    const ExampleItemLayerBase *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.offset = 0;
        format.redChannel.length = 5;
        format.greenChannel.offset = 5;
        format.greenChannel.length = 6;
        format.blueChannel.offset = 11;
        format.blueChannel.length = 5;
        format.swapBytes = 2;
        break;
    case 32:
        format.redChannel.offset = 16;
        format.redChannel.length = 8;
        format.greenChannel.offset = 8;
        format.greenChannel.length = 8;
        format.blueChannel.offset = 0;
        format.blueChannel.length = 8;
        format.swapBytes = 0;
        break;
    default:
        QUL_ASSERT(false, QulError_LayerEngine_UnsupportedColorDepth, format.bitsPerPixel);
    }

    return format;
}

To allow Qt Quick Ultralite to use your custom device link implementation, create a function to return the device link interface for your device.

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

See also QUL_PLATFORM_DEVICELINK_ENABLED.

Hardware setup

In your implementation of PlatformContext::initializeHardware, add the initialization of the device link library after initializing the hardware of the desired channel, which is usually a serial port,

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

This will also call the platformInit function implemented above to start data reception.

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

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

Avoid writing any other data to the serial port, because this will interfere with the transfer protocol. To be sure, reimplement the low-level write functions that printf or others may use, to direct their content through the device link 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. All messages printed through the Qt Quick Ultralite logging system are automatically transferred via device link when active.

int _write(char *message, int len) {
    auto deviceLink = Qul::Platform::DeviceLink::instance();
    if (deviceLink)
        deviceLink->printMessage(message, len);
    else
        sendDataDirectlyToSerialPort();
}

Updating the logging implementation

Modify the consoleWrite function implemented in an earlier step to use the device link transfer as well.

void ExamplePlatform::consoleWrite(char character) {
    if (deviceLink) {
        // You should think about collecting several characters and send them in chunks
        // instead of 1 by 1 because this will be slow.
        deviceLink->printMessage(&character, 1);
    } else {
        // Write the charactor to your boards output device
        sendDataDirectlyToSerialPort();
    }
}

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.