Skip to content

Commit 662d591

Browse files
committed
[VirtIO] Add first VirtIO and ringbuffer
1 parent e27a95c commit 662d591

22 files changed

+772
-435
lines changed

boards.txt

+9-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ menu.pnum=Board part number
55
menu.xserial=U(S)ART support
66
menu.usb=USB support (if available)
77
menu.xusb=USB speed (if available)
8+
menu.virtio=Virtual serial support
89

910
menu.opt=Optimize
1011
menu.rtlib=C Runtime Library
@@ -707,7 +708,7 @@ STM32MP1.build.mcu=cortex-m4
707708
STM32MP1.build.flags.fp=-mfpu=fpv4-sp-d16 -mfloat-abi=hard
708709
STM32MP1.build.series=STM32MP1xx
709710
STM32MP1.build.cmsis_lib_gcc=arm_cortexM4l_math
710-
STM32MP1.build.extra_flags=-DCORE_CM4 -DUSE_FULL_LL_DRIVER -D{build.product_line} {build.xSerial}
711+
STM32MP1.build.extra_flags=-DCORE_CM4 -DUSE_FULL_LL_DRIVER -D{build.product_line} {build.enable_virtio} {build.xSerial}
711712

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

1890+
STM32MP1.menu.virtio.disable=Disabled (no SerialVirtIO nor /dev/ttyRPMSG0 available)
1891+
STM32MP1.menu.virtio.disable.build.enable_virtio=
1892+
STM32MP1.menu.virtio.generic=SerialVirtIO (= generic 'Serial') <=> /dev/ttyRPMSG0
1893+
STM32MP1.menu.virtio.generic.build.enable_virtio={build.virtio_flags}
1894+
STM32MP1.menu.virtio.enabled=SerialVirtIO <=> /dev/ttyRPMSG0
1895+
STM32MP1.menu.virtio.enabled.build.enable_virtio={build.virtio_flags} -DDISABLE_GENERIC_SERIALVIRTIO
1896+
18891897
STM32MP1.menu.xserial.generic=UART only (generic 'Serial')
18901898
STM32MP1.menu.xserial.generic.build.xSerial=-DHAL_UART_MODULE_ENABLED
18911899
STM32MP1.menu.xserial.none=UART only (no generic 'Serial')

cores/arduino/VirtIOSerial.cpp

+217
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/**
2+
* MIT License:
3+
* Copyright (c) 2019 Bumsik kim <[email protected]>
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
#if defined (VIRTIOCON)
25+
26+
#include "VirtIOSerial.h"
27+
#include "core_debug.h"
28+
29+
#if !defined(VIRTIOSERIAL_NUM)
30+
#define VIRTIOSERIAL_NUM 1
31+
#endif
32+
33+
VirtIOSerialObj_t *VirtIOSerial_Handle[VIRTIOSERIAL_NUM] = {NULL};
34+
35+
uint32_t VirtIOSerial::VirtIOSerial_index = 0;
36+
37+
// Default instance
38+
VirtIOSerial SerialVirtIO;
39+
40+
void serialEventVirtIO() __attribute__((weak));
41+
VirtIOSerialObj_t *get_VirtIOSerial_obj(VIRT_UART_HandleTypeDef *huart);
42+
43+
void VirtIOSerial::begin(void)
44+
{
45+
virtio_buffer_init(&_VirtIOSerialObj.ring);
46+
if (_VirtIOSerialObj.initialized) {
47+
return;
48+
}
49+
if (OPENAMP_Init() != 0) {
50+
Error_Handler();
51+
}
52+
if (VIRT_UART_Init(&_VirtIOSerialObj.handle) != VIRT_UART_OK) {
53+
Error_Handler();
54+
}
55+
56+
VirtIOSerial_index ++;
57+
_VirtIOSerialObj.__this = (void *)this;
58+
59+
/* Need to register callback for message reception by channels */
60+
if (VIRT_UART_RegisterCallback(&_VirtIOSerialObj.handle, VIRT_UART_RXCPLT_CB_ID, rxGenericCallback) != VIRT_UART_OK) {
61+
Error_Handler();
62+
}
63+
64+
VirtIOSerial_Handle[VirtIOSerial_index] = &_VirtIOSerialObj;
65+
_VirtIOSerialObj.initialized = true;
66+
_VirtIOSerialObj.first_message_discarded = false;
67+
68+
// This will wait for the first message "DUMMY", see rxCallback().
69+
OPENAMP_Wait_EndPointready(&_VirtIOSerialObj.handle.ept);
70+
}
71+
72+
void VirtIOSerial::begin(uint32_t /* baud_count */)
73+
{
74+
// uart config is ignored in OpenAMP
75+
begin();
76+
}
77+
78+
void VirtIOSerial::begin(uint32_t /* baud_count */, uint8_t /* config */)
79+
{
80+
// uart config is ignored in OpenAMP
81+
begin();
82+
}
83+
84+
void VirtIOSerial::end()
85+
{
86+
VIRT_UART_DeInit(&_VirtIOSerialObj.handle);
87+
OPENAMP_DeInit();
88+
virtio_buffer_init(&_VirtIOSerialObj.ring);
89+
_VirtIOSerialObj.initialized = false;
90+
}
91+
92+
int VirtIOSerial::available(void)
93+
{
94+
checkMessageFromISR();
95+
return virtio_buffer_read_available(&_VirtIOSerialObj.ring);
96+
}
97+
98+
int VirtIOSerial::availableForWrite()
99+
{
100+
checkMessageFromISR();
101+
// Just return max length of VIRT_UART_Transmit() can transmit.
102+
// See VIRT_UART_Transmit().
103+
return RPMSG_VRING_PAYLOAD_SIZE;
104+
}
105+
106+
int VirtIOSerial::peek(void)
107+
{
108+
checkMessageFromISR();
109+
if (virtio_buffer_read_available(&_VirtIOSerialObj.ring) > 0) {
110+
uint8_t tmp;
111+
virtio_buffer_peek(&_VirtIOSerialObj.ring, &tmp, 1);
112+
return tmp;
113+
} else {
114+
return -1;
115+
}
116+
}
117+
118+
int VirtIOSerial::read(void)
119+
{
120+
if (available() > 0) {
121+
char ch;
122+
readBytes(&ch, 1);
123+
return ch;
124+
} else {
125+
return -1;
126+
}
127+
}
128+
129+
size_t VirtIOSerial::readBytes(char *buffer, size_t length)
130+
{
131+
checkMessageFromISR();
132+
const size_t size = virtio_buffer_read(&_VirtIOSerialObj.ring, reinterpret_cast<uint8_t *>(buffer), length);
133+
// The ring buffer might be available enough to write after reading
134+
checkMessageFromISR();
135+
return size;
136+
}
137+
138+
size_t VirtIOSerial::write(uint8_t ch)
139+
{
140+
// Just write single-byte buffer.
141+
return write(&ch, 1);
142+
}
143+
144+
// Warning: Currently VirtIOSerial implementation is synchronous, blocking
145+
// until all bytes are sent. But it will be fast enough.
146+
size_t VirtIOSerial::write(const uint8_t *buffer, size_t size)
147+
{
148+
checkMessageFromISR();
149+
if (VIRT_UART_Transmit(&_VirtIOSerialObj.handle, const_cast<uint8_t *>(buffer), size) != VIRT_UART_OK) {
150+
// This error usually happens when size > 496. See VirtIOSerial::availableForWrite()
151+
core_debug("ERROR: VirtIOSerial::write() failed. Check availableForWrite().\n");
152+
return 0;
153+
}
154+
// It is likely receive "buf free" from the Linux host right after
155+
// VIRT_UART_Transmit(). So check it here too.
156+
checkMessageFromISR();
157+
return size;
158+
}
159+
160+
void VirtIOSerial::flush(void)
161+
{
162+
checkMessageFromISR();
163+
// write() is blocked until all bytes are sent. So flush() doesn't need to do
164+
// anything. See rpmsg_send().
165+
return;
166+
}
167+
168+
/**
169+
* @brief Check if RPMsg message arrived from IPCC ISR
170+
* @note This should called as mush as possible to consume RPMsg ring buffers,
171+
* so that the host processor can send more messages quickly.
172+
*/
173+
void VirtIOSerial::checkMessageFromISR(void)
174+
{
175+
OPENAMP_check_for_tx_message();
176+
if (virtio_buffer_write_available(&_VirtIOSerialObj.ring) >= RPMSG_VRING_TOTAL_PAYLOAD_SIZE) {
177+
// This calls rxCallback() VRING_NUM_BUFFS times at maximum
178+
OPENAMP_check_for_rx_message();
179+
}
180+
}
181+
182+
void VirtIOSerial::rxGenericCallback(VIRT_UART_HandleTypeDef *huart)
183+
{
184+
VirtIOSerialObj_t *obj = get_VirtIOSerial_obj(huart);
185+
VirtIOSerial *VIOS = (VirtIOSerial *)(obj->__this);
186+
187+
VIOS->rxCallback(huart);
188+
}
189+
190+
void VirtIOSerial::rxCallback(VIRT_UART_HandleTypeDef *huart)
191+
{
192+
// Linux host must send a dummy data first to finish initialization of rpmsg
193+
// on the coprocessor side. This message should be discarded.
194+
// run_arduino_gen.sh script will send dummy data: "DUMMY".
195+
// See: https://github.com/OpenAMP/open-amp/issues/182
196+
// See: run_arduino_gen.sh
197+
if (!_VirtIOSerialObj.first_message_discarded) {
198+
huart->RxXferSize = 0;
199+
_VirtIOSerialObj.first_message_discarded = true;
200+
}
201+
202+
size_t size = min(huart->RxXferSize, virtio_buffer_write_available(&_VirtIOSerialObj.ring));
203+
while (size > 0) {
204+
size -= virtio_buffer_write(&_VirtIOSerialObj.ring, huart->pRxBuffPtr, size);
205+
}
206+
}
207+
208+
/* Aim of the function is to get _VirtIOSerialObj pointer using huart pointer */
209+
/* Highly inspired from magical linux kernel's "container_of" */
210+
VirtIOSerialObj_t *get_VirtIOSerial_obj(VIRT_UART_HandleTypeDef *huart)
211+
{
212+
VirtIOSerialObj_t *obj;
213+
obj = (VirtIOSerialObj_t *)((char *)huart - offsetof(VirtIOSerialObj_t, handle));
214+
return (obj);
215+
}
216+
217+
#endif /* VIRTIOCON */

cores/arduino/VirtIOSerial.h

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* MIT License:
3+
* Copyright (c) 2019 Bumsik kim <[email protected]>
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
#ifndef _VIRTIOSERIAL_H_
25+
#define _VIRTIOSERIAL_H_
26+
27+
#if defined (VIRTIOCON)
28+
#include "Stream.h"
29+
#include "openamp.h"
30+
#include "openamp_log.h"
31+
#include "wiring.h"
32+
#include "virtio_buffer.h"
33+
34+
//================================================================================
35+
// Serial over OpenAMP
36+
37+
// This structure is used to be able to get VirtIOSerial instance (C++ class)
38+
// from handler (C structure) specially for rpmsg message management
39+
typedef struct {
40+
// Those 2 first fields must remain in this order at the beginning of the structure
41+
void *__this;
42+
VIRT_UART_HandleTypeDef handle;
43+
bool initialized;
44+
bool first_message_discarded;
45+
virtio_buffer_t ring;
46+
} VirtIOSerialObj_t;
47+
48+
49+
class VirtIOSerial : public Stream {
50+
public:
51+
void begin(void);
52+
void begin(uint32_t);
53+
void begin(uint32_t, uint8_t);
54+
void end(void);
55+
56+
virtual int available(void);
57+
virtual int availableForWrite(void);
58+
virtual int peek(void);
59+
virtual int read(void);
60+
virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer
61+
virtual size_t write(uint8_t);
62+
virtual size_t write(const uint8_t *buffer, size_t size);
63+
virtual void flush(void);
64+
65+
static void rxGenericCallback(VIRT_UART_HandleTypeDef *huart);
66+
void rxCallback(VIRT_UART_HandleTypeDef *huart);
67+
68+
using Print::write; // pull in write(str) from Print
69+
operator bool(void)
70+
{
71+
return true;
72+
}
73+
74+
private:
75+
static uint32_t VirtIOSerial_index;
76+
VirtIOSerialObj_t _VirtIOSerialObj;
77+
void checkMessageFromISR(void);
78+
};
79+
80+
extern VirtIOSerial SerialVirtIO;
81+
82+
#endif /* VIRTIOCON */
83+
#endif /* _VIRTIOSERIAL_H_ */

cores/arduino/WSerial.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,10 @@ WEAK void serialEventRun(void)
6262
serialEventUSB();
6363
}
6464
#endif
65+
#if defined(HAVE_SERIALVIRTIO)
66+
if (serialEventVirtIO && SerialVirtIO.available()) {
67+
serialEventVirtIO();
68+
}
69+
#endif
6570
}
6671

cores/arduino/WSerial.h

+17
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "variant.h"
55
#include "HardwareSerial.h"
66
#include "USBSerial.h"
7+
#include "VirtIOSerial.h"
78

89
#if defined (USBCON) && defined(USBD_USE_CDC)
910
#ifndef DISABLE_GENERIC_SERIALUSB
@@ -21,6 +22,22 @@
2122
extern void serialEventUSB(void) __attribute__((weak));
2223
#endif /* USBCON && USBD_USE_CDC */
2324

25+
#if defined(VIRTIOCON)
26+
#ifndef DISABLE_GENERIC_SERIALVIRTIO
27+
#define ENABLE_SERIALVIRTIO
28+
#if !defined(Serial)
29+
#define Serial SerialVirtIO
30+
#define serialEvent serialEventVirtIO
31+
#endif
32+
#endif
33+
34+
#if defined(ENABLE_SERIALVIRTIO)
35+
#define HAVE_SERIALVIRTIO
36+
#endif
37+
38+
extern void serialEventVirtIO(void) __attribute__((weak));
39+
#endif /* VIRTIOCON */
40+
2441
#if defined(HAL_UART_MODULE_ENABLED) && !defined(HAL_UART_MODULE_ONLY)
2542
#if !defined(HWSERIAL_NONE) && defined(SERIAL_UART_INSTANCE)
2643
#if SERIAL_UART_INSTANCE == 0

0 commit comments

Comments
 (0)