Skip to content

Commit

Permalink
Merge pull request #766 from kbumsik/virtio
Browse files Browse the repository at this point in the history
[MP1] Add RPMsg virtual serial protocol support (VirtIOSerial)
  • Loading branch information
fpistm committed Mar 17, 2020
2 parents 0203e2d + a6dcefe commit df6b4f7
Show file tree
Hide file tree
Showing 43 changed files with 1,682 additions and 9 deletions.
8 changes: 8 additions & 0 deletions CI/build/conf/cores_config.json
Expand Up @@ -11,6 +11,14 @@
{
"name": "STEVAL_MKSBOX1V1",
"options": "usb=CDCgen"
},
{
"name": "STM32MP157A_DK1",
"options": "virtio=enabled"
},
{
"name": "STM32MP157C_DK2",
"options": "virtio=generic"
}
],
"sketches": [
Expand Down
8 changes: 8 additions & 0 deletions CI/build/conf/cores_config_ci.json
Expand Up @@ -11,6 +11,14 @@
{
"name": "STEVAL_MKSBOX1V1",
"options": "usb=CDCgen"
},
{
"name": "STM32MP157A_DK1",
"options": "virtio=enabled"
},
{
"name": "STM32MP157C_DK2",
"options": "virtio=generic"
}
],
"sketches": [
Expand Down
8 changes: 8 additions & 0 deletions CI/build/conf/cores_config_travis.json
Expand Up @@ -7,6 +7,14 @@
{
"name": "NUCLEO_L031K6",
"options": "opt=oslto"
},
{
"name": "STM32MP157A_DK1",
"options": "virtio=enabled"
},
{
"name": "STM32MP157C_DK2",
"options": "virtio=generic"
}
],
"sketches": [
Expand Down
3 changes: 2 additions & 1 deletion CI/build/examples/BareMinimum/BareMinimum.ino
Expand Up @@ -42,7 +42,8 @@ SoftwareSerial swSerial(10, 11);

void setup() {
// Serial HW & SW
#if !defined(USBD_USE_CDC) && !defined(DISABLE_GENERIC_SERIALUSB)
#if (!defined(USBD_USE_CDC) && !defined(DISABLE_GENERIC_SERIALUSB)) &&\
(!defined(VIRTIOCON) && !defined(DISABLE_GENERIC_SERIALVIRTIO))
Serial.setRx(PIN_SERIAL_RX);
Serial.setTx(digitalPinToPinName(PIN_SERIAL_TX));
#endif
Expand Down
10 changes: 9 additions & 1 deletion boards.txt
Expand Up @@ -5,6 +5,7 @@ menu.pnum=Board part number
menu.xserial=U(S)ART support
menu.usb=USB support (if available)
menu.xusb=USB speed (if available)
menu.virtio=Virtual serial support

menu.opt=Optimize
menu.rtlib=C Runtime Library
Expand Down Expand Up @@ -707,7 +708,7 @@ STM32MP1.build.mcu=cortex-m4
STM32MP1.build.flags.fp=-mfpu=fpv4-sp-d16 -mfloat-abi=hard
STM32MP1.build.series=STM32MP1xx
STM32MP1.build.cmsis_lib_gcc=arm_cortexM4l_math
STM32MP1.build.extra_flags=-DCORE_CM4 -DUSE_FULL_LL_DRIVER -D{build.product_line} {build.xSerial}
STM32MP1.build.extra_flags=-DCORE_CM4 -DUSE_FULL_LL_DRIVER -D{build.product_line} {build.enable_virtio} {build.xSerial}

# STM32MP157A-DK1 board
STM32MP1.menu.pnum.STM32MP157A_DK1=STM32MP157A-DK1
Expand Down Expand Up @@ -1886,6 +1887,13 @@ Eval.menu.xserial.none.build.xSerial=-DHAL_UART_MODULE_ENABLED -DHWSERIAL_NONE
Eval.menu.xserial.disabled=Disabled (no Serial support)
Eval.menu.xserial.disabled.build.xSerial=

STM32MP1.menu.virtio.disable=Disabled (no SerialVirtIO nor /dev/ttyRPMSG0 available)
STM32MP1.menu.virtio.disable.build.enable_virtio=
STM32MP1.menu.virtio.generic=SerialVirtIO (= generic 'Serial') <=> /dev/ttyRPMSG0
STM32MP1.menu.virtio.generic.build.enable_virtio={build.virtio_flags}
STM32MP1.menu.virtio.enabled=SerialVirtIO <=> /dev/ttyRPMSG0
STM32MP1.menu.virtio.enabled.build.enable_virtio={build.virtio_flags} -DDISABLE_GENERIC_SERIALVIRTIO

STM32MP1.menu.xserial.generic=UART only (generic 'Serial')
STM32MP1.menu.xserial.generic.build.xSerial=-DHAL_UART_MODULE_ENABLED
STM32MP1.menu.xserial.none=UART only (no generic 'Serial')
Expand Down
9 changes: 8 additions & 1 deletion cores/arduino/Print.cpp
Expand Up @@ -28,6 +28,10 @@

#include "Print.h"

#if defined (VIRTIO_LOG)
#include "virtio_log.h"
#endif

// Public Methods //////////////////////////////////////////////////////////////

/* default implementation: may be overridden */
Expand Down Expand Up @@ -203,10 +207,13 @@ extern "C" {
switch (file) {
case STDOUT_FILENO:
case STDERR_FILENO:
#if defined(HAL_UART_MODULE_ENABLED) && !defined(HAL_UART_MODULE_ONLY)
/* Used for core_debug() */
#if defined (VIRTIO_LOG)
virtio_log((uint8_t *)ptr, (uint32_t)len);
#elif defined(HAL_UART_MODULE_ENABLED) && !defined(HAL_UART_MODULE_ONLY)
uart_debug_write((uint8_t *)ptr, (uint32_t)len);
#endif
break;
case STDIN_FILENO:
break;
default:
Expand Down
217 changes: 217 additions & 0 deletions cores/arduino/VirtIOSerial.cpp
@@ -0,0 +1,217 @@
/**
* MIT License:
* Copyright (c) 2019 Bumsik kim <k.bumsik@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#if defined (VIRTIOCON)

#include "VirtIOSerial.h"
#include "core_debug.h"

#if !defined(VIRTIOSERIAL_NUM)
#define VIRTIOSERIAL_NUM 1
#endif

VirtIOSerialObj_t *VirtIOSerial_Handle[VIRTIOSERIAL_NUM] = {NULL};

uint32_t VirtIOSerial::VirtIOSerial_index = 0;

// Default instance
VirtIOSerial SerialVirtIO;

void serialEventVirtIO() __attribute__((weak));
VirtIOSerialObj_t *get_VirtIOSerial_obj(VIRT_UART_HandleTypeDef *huart);

void VirtIOSerial::begin(void)
{
virtio_buffer_init(&_VirtIOSerialObj.ring);
if (_VirtIOSerialObj.initialized) {
return;
}
if (OPENAMP_Init() != 0) {
Error_Handler();
}
if (VIRT_UART_Init(&_VirtIOSerialObj.handle) != VIRT_UART_OK) {
Error_Handler();
}

VirtIOSerial_index ++;
_VirtIOSerialObj.__this = (void *)this;

/* Need to register callback for message reception by channels */
if (VIRT_UART_RegisterCallback(&_VirtIOSerialObj.handle, VIRT_UART_RXCPLT_CB_ID, rxGenericCallback) != VIRT_UART_OK) {
Error_Handler();
}

VirtIOSerial_Handle[VirtIOSerial_index] = &_VirtIOSerialObj;
_VirtIOSerialObj.initialized = true;
_VirtIOSerialObj.first_message_discarded = false;

// This will wait for the first message "DUMMY", see rxCallback().
OPENAMP_Wait_EndPointready(&_VirtIOSerialObj.handle.ept);
}

void VirtIOSerial::begin(uint32_t /* baud_count */)
{
// uart config is ignored in OpenAMP
begin();
}

void VirtIOSerial::begin(uint32_t /* baud_count */, uint8_t /* config */)
{
// uart config is ignored in OpenAMP
begin();
}

void VirtIOSerial::end()
{
VIRT_UART_DeInit(&_VirtIOSerialObj.handle);
OPENAMP_DeInit();
virtio_buffer_init(&_VirtIOSerialObj.ring);
_VirtIOSerialObj.initialized = false;
}

int VirtIOSerial::available(void)
{
checkMessageFromISR();
return virtio_buffer_read_available(&_VirtIOSerialObj.ring);
}

int VirtIOSerial::availableForWrite()
{
checkMessageFromISR();
// Just return max length of VIRT_UART_Transmit() can transmit.
// See VIRT_UART_Transmit().
return RPMSG_VRING_PAYLOAD_SIZE;
}

int VirtIOSerial::peek(void)
{
checkMessageFromISR();
if (virtio_buffer_read_available(&_VirtIOSerialObj.ring) > 0) {
uint8_t tmp;
virtio_buffer_peek(&_VirtIOSerialObj.ring, &tmp, 1);
return tmp;
} else {
return -1;
}
}

int VirtIOSerial::read(void)
{
if (available() > 0) {
char ch;
readBytes(&ch, 1);
return ch;
} else {
return -1;
}
}

size_t VirtIOSerial::readBytes(char *buffer, size_t length)
{
checkMessageFromISR();
const size_t size = virtio_buffer_read(&_VirtIOSerialObj.ring, reinterpret_cast<uint8_t *>(buffer), length);
// The ring buffer might be available enough to write after reading
checkMessageFromISR();
return size;
}

size_t VirtIOSerial::write(uint8_t ch)
{
// Just write single-byte buffer.
return write(&ch, 1);
}

// Warning: Currently VirtIOSerial implementation is synchronous, blocking
// until all bytes are sent. But it will be fast enough.
size_t VirtIOSerial::write(const uint8_t *buffer, size_t size)
{
checkMessageFromISR();
if (VIRT_UART_Transmit(&_VirtIOSerialObj.handle, const_cast<uint8_t *>(buffer), size) != VIRT_UART_OK) {
// This error usually happens when size > 496. See VirtIOSerial::availableForWrite()
core_debug("ERROR: VirtIOSerial::write() failed. Check availableForWrite().\n");
return 0;
}
// It is likely receive "buf free" from the Linux host right after
// VIRT_UART_Transmit(). So check it here too.
checkMessageFromISR();
return size;
}

void VirtIOSerial::flush(void)
{
checkMessageFromISR();
// write() is blocked until all bytes are sent. So flush() doesn't need to do
// anything. See rpmsg_send().
return;
}

/**
* @brief Check if RPMsg message arrived from IPCC ISR
* @note This should called as mush as possible to consume RPMsg ring buffers,
* so that the host processor can send more messages quickly.
*/
void VirtIOSerial::checkMessageFromISR(void)
{
OPENAMP_check_for_tx_message();
if (virtio_buffer_write_available(&_VirtIOSerialObj.ring) >= RPMSG_VRING_TOTAL_PAYLOAD_SIZE) {
// This calls rxCallback() VRING_NUM_BUFFS times at maximum
OPENAMP_check_for_rx_message();
}
}

void VirtIOSerial::rxGenericCallback(VIRT_UART_HandleTypeDef *huart)
{
VirtIOSerialObj_t *obj = get_VirtIOSerial_obj(huart);
VirtIOSerial *VIOS = (VirtIOSerial *)(obj->__this);

VIOS->rxCallback(huart);
}

void VirtIOSerial::rxCallback(VIRT_UART_HandleTypeDef *huart)
{
// Linux host must send a dummy data first to finish initialization of rpmsg
// on the coprocessor side. This message should be discarded.
// run_arduino_gen.sh script will send dummy data: "DUMMY".
// See: https://github.com/OpenAMP/open-amp/issues/182
// See: run_arduino_gen.sh
if (!_VirtIOSerialObj.first_message_discarded) {
huart->RxXferSize = 0;
_VirtIOSerialObj.first_message_discarded = true;
}

size_t size = min(huart->RxXferSize, virtio_buffer_write_available(&_VirtIOSerialObj.ring));
while (size > 0) {
size -= virtio_buffer_write(&_VirtIOSerialObj.ring, huart->pRxBuffPtr, size);
}
}

/* Aim of the function is to get _VirtIOSerialObj pointer using huart pointer */
/* Highly inspired from magical linux kernel's "container_of" */
VirtIOSerialObj_t *get_VirtIOSerial_obj(VIRT_UART_HandleTypeDef *huart)
{
VirtIOSerialObj_t *obj;
obj = (VirtIOSerialObj_t *)((char *)huart - offsetof(VirtIOSerialObj_t, handle));
return (obj);
}

#endif /* VIRTIOCON */

0 comments on commit df6b4f7

Please sign in to comment.