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.
Custom Device Link
The custom device link implementation provides the interface between the protocol facilities and the hardware. The two following functions must be implemented:
- Qul::Platform::DeviceLinkInterface::platformInit enables the actual receiving process on the serial port.
- Qul::Platform::DeviceLinkInterface::transmitChars writes data that is ready to be sent to the serial port.
- Qul::Platform::DeviceLinkInterface::framebufferFormat retrieves information about the framebuffer of a given layer.
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 { // 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.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; } };
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) { 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; }
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.
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.