summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuEe-GUI <[email protected]>2026-03-01 13:11:32 +0800
committerRbb666 <[email protected]>2026-06-15 10:00:57 +0800
commited332e4aa91b2e60ff9175222423f0c8e72eff1f (patch)
tree07637ce23663c87d9bf66794c1a292e612a58491
parenta777a34758a72a037858f26e90e5e5a5f628bc81 (diff)
[bsp][qemu-virt64-aarch64] support full dm
Signed-off-by: GuEe-GUI <[email protected]>
-rw-r--r--bsp/qemu-virt64-aarch64/.config186
-rwxr-xr-xbsp/qemu-virt64-aarch64/.gitignore2
-rw-r--r--bsp/qemu-virt64-aarch64/Kconfig13
-rwxr-xr-xbsp/qemu-virt64-aarch64/amp.dts111
-rwxr-xr-xbsp/qemu-virt64-aarch64/amp.dtsi108
-rw-r--r--bsp/qemu-virt64-aarch64/applications/SConscript6
-rwxr-xr-xbsp/qemu-virt64-aarch64/applications/amp.c277
-rwxr-xr-xbsp/qemu-virt64-aarch64/applications/camera.c207
-rw-r--r--bsp/qemu-virt64-aarch64/applications/console.c33
-rw-r--r--bsp/qemu-virt64-aarch64/applications/graphic.c224
-rw-r--r--bsp/qemu-virt64-aarch64/applications/mnt.c41
-rw-r--r--bsp/qemu-virt64-aarch64/applications/pin.c33
-rwxr-xr-x[-rw-r--r--]bsp/qemu-virt64-aarch64/drivers/Kconfig91
-rwxr-xr-x[-rw-r--r--]bsp/qemu-virt64-aarch64/drivers/SConscript8
-rw-r--r--bsp/qemu-virt64-aarch64/drivers/board.c6
-rw-r--r--bsp/qemu-virt64-aarch64/drivers/board.h9
-rwxr-xr-xbsp/qemu-virt64-aarch64/drivers/drv_camera.c280
-rwxr-xr-xbsp/qemu-virt64-aarch64/drivers/drv_camera.h21
-rw-r--r--bsp/qemu-virt64-aarch64/drivers/drv_gpio.c318
-rw-r--r--bsp/qemu-virt64-aarch64/drivers/drv_gpio.h16
-rwxr-xr-xbsp/qemu-virt64-aarch64/drivers/drv_romfb.c17
-rw-r--r--bsp/qemu-virt64-aarch64/drivers/drv_rtc.c126
-rw-r--r--bsp/qemu-virt64-aarch64/drivers/drv_rtc.h25
-rw-r--r--bsp/qemu-virt64-aarch64/drivers/drv_uart.c146
-rw-r--r--bsp/qemu-virt64-aarch64/drivers/drv_uart.h16
-rw-r--r--bsp/qemu-virt64-aarch64/drivers/drv_virtio.c93
-rw-r--r--bsp/qemu-virt64-aarch64/drivers/drv_virtio.h16
-rw-r--r--bsp/qemu-virt64-aarch64/drivers/virt.h96
-rw-r--r--bsp/qemu-virt64-aarch64/qemu-debug.bat9
-rw-r--r--bsp/qemu-virt64-aarch64/qemu-debug.sh8
-rwxr-xr-xbsp/qemu-virt64-aarch64/qemu-device-camera.c709
-rw-r--r--bsp/qemu-virt64-aarch64/qemu-graphic.bat13
-rw-r--r--bsp/qemu-virt64-aarch64/qemu-graphic.sh12
-rw-r--r--bsp/qemu-virt64-aarch64/qemu.bat9
-rwxr-xr-xbsp/qemu-virt64-aarch64/qemu.py380
-rwxr-xr-xbsp/qemu-virt64-aarch64/qemu.sh8
-rw-r--r--bsp/qemu-virt64-aarch64/rtconfig.h123
-rwxr-xr-xcomponents/drivers/rpmsg/Kconfig6
-rwxr-xr-xcomponents/drivers/rpmsg/SConscript3
-rw-r--r--components/drivers/rpmsg/rpmsg-rt-thread-virtio.c565
40 files changed, 2926 insertions, 1444 deletions
diff --git a/bsp/qemu-virt64-aarch64/.config b/bsp/qemu-virt64-aarch64/.config
index 696b5c11ab..9aa0a43b0c 100644
--- a/bsp/qemu-virt64-aarch64/.config
+++ b/bsp/qemu-virt64-aarch64/.config
@@ -11,18 +11,8 @@
# rt_vsnprintf options
#
# CONFIG_RT_KLIBC_USING_LIBC_VSNPRINTF is not set
-CONFIG_RT_KLIBC_USING_VSNPRINTF_LONGLONG=y
-CONFIG_RT_KLIBC_USING_VSNPRINTF_STANDARD=y
-CONFIG_RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS=y
-CONFIG_RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS=y
-CONFIG_RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER=y
-CONFIG_RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER=y
-# CONFIG_RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS is not set
-CONFIG_RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE=32
-CONFIG_RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE=32
-CONFIG_RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION=6
-CONFIG_RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL=9
-CONFIG_RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS=4
+# CONFIG_RT_KLIBC_USING_VSNPRINTF_LONGLONG is not set
+# CONFIG_RT_KLIBC_USING_VSNPRINTF_STANDARD is not set
# end of rt_vsnprintf options
#
@@ -292,7 +282,7 @@ CONFIG_RT_DFS_ELM_MUTEX_TIMEOUT=3000
# end of elm-chan's FatFs, Generic FAT Filesystem Module
CONFIG_RT_USING_DFS_DEVFS=y
-# CONFIG_RT_USING_DFS_9PFS is not set
+CONFIG_RT_USING_DFS_9PFS=y
# CONFIG_RT_USING_DFS_ISO9660 is not set
CONFIG_RT_USING_DFS_ROMFS=y
# CONFIG_RT_USING_DFS_ROMFS_USER_ROOT is not set
@@ -321,11 +311,14 @@ CONFIG_RT_SERIAL_USING_DMA=y
CONFIG_RT_SERIAL_RB_BUFSZ=256
# CONFIG_RT_USING_SERIAL_BYPASS is not set
# CONFIG_RT_SERIAL_EARLY_HVC is not set
-# CONFIG_RT_SERIAL_PL011 is not set
-# CONFIG_RT_SERIAL_8250 is not set
+CONFIG_RT_SERIAL_PL011=y
+CONFIG_RT_SERIAL_8250=y
+# CONFIG_RT_SERIAL_8250_DW is not set
+CONFIG_RT_SERIAL_8250_PCI=y
+# CONFIG_RT_SERIAL_VIRTUAL is not set
# CONFIG_RT_USING_CAN is not set
CONFIG_RT_USING_CLOCK_TIME=y
-# CONFIG_RT_CLOCK_TIME_ARM_ARCH is not set
+CONFIG_RT_CLOCK_TIME_ARM_ARCH=y
# CONFIG_RT_USING_I2C is not set
# CONFIG_RT_USING_PHY is not set
# CONFIG_RT_USING_PHY_V2 is not set
@@ -337,38 +330,94 @@ CONFIG_RT_USING_RANDOM=y
# CONFIG_RT_USING_PWM is not set
# CONFIG_RT_USING_PULSE_ENCODER is not set
# CONFIG_RT_USING_INPUT_CAPTURE is not set
-# CONFIG_RT_USING_MTD_NOR is not set
+CONFIG_RT_USING_MTD_NOR=y
+CONFIG_RT_USING_MTD_NOR_CFI=y
# CONFIG_RT_USING_MTD_NAND is not set
-CONFIG_RT_USING_PM=y
-CONFIG_PM_TICKLESS_THRESHOLD_TIME=2
-# CONFIG_PM_USING_CUSTOM_CONFIG is not set
-# CONFIG_PM_ENABLE_DEBUG is not set
-# CONFIG_PM_ENABLE_SUSPEND_SLEEP_MODE is not set
-# CONFIG_PM_ENABLE_THRESHOLD_SLEEP_MODE is not set
+# CONFIG_RT_USING_PM is not set
CONFIG_RT_USING_RTC=y
-# CONFIG_RT_USING_ALARM is not set
-CONFIG_RT_USING_SOFT_RTC=y
+CONFIG_RT_USING_ALARM=y
+CONFIG_RT_ALARM_STACK_SIZE=8192
+CONFIG_RT_ALARM_TIMESLICE=5
+CONFIG_RT_ALARM_PRIORITY=10
+# CONFIG_RT_ALARM_USING_LOCAL_TIME is not set
+# CONFIG_RT_USING_SOFT_RTC is not set
# CONFIG_RT_RTC_GOLDFISH is not set
-# CONFIG_RT_RTC_PL031 is not set
+CONFIG_RT_RTC_PL031=y
# CONFIG_RT_USING_SDIO is not set
# CONFIG_RT_USING_SPI is not set
-# CONFIG_RT_USING_WDT is not set
+CONFIG_RT_USING_WDT=y
+CONFIG_RT_WDT_I6300ESB=y
# CONFIG_RT_USING_AUDIO is not set
# CONFIG_RT_USING_SENSOR is not set
# CONFIG_RT_USING_TOUCH is not set
-# CONFIG_RT_USING_GRAPHIC is not set
-# CONFIG_RT_USING_HWCRYPTO is not set
+CONFIG_RT_USING_LCD=y
+CONFIG_RT_USING_GRAPHIC=y
+# CONFIG_RT_GRAPHIC_BACKLIGHT is not set
+CONFIG_RT_GRAPHIC_FB=y
+# CONFIG_RT_GRAPHIC_FB_SIMPLE is not set
+CONFIG_RT_GRAPHIC_LOGO=y
+# CONFIG_RT_GRAPHIC_LOGO_NONE is not set
+CONFIG_RT_GRAPHIC_LOGO_RT_THREAD_CLUT224=y
+# CONFIG_RT_GRAPHIC_LOGO_RT_THREAD_WHITE_CLUT224 is not set
+CONFIG_RT_USING_HWCRYPTO=y
+CONFIG_RT_HWCRYPTO_DEFAULT_NAME="hwcryto"
+CONFIG_RT_HWCRYPTO_IV_MAX_SIZE=16
+CONFIG_RT_HWCRYPTO_KEYBIT_MAX_SIZE=256
+# CONFIG_RT_HWCRYPTO_USING_GCM is not set
+CONFIG_RT_HWCRYPTO_USING_AES=y
+CONFIG_RT_HWCRYPTO_USING_AES_ECB=y
+CONFIG_RT_HWCRYPTO_USING_AES_CBC=y
+# CONFIG_RT_HWCRYPTO_USING_AES_CFB is not set
+CONFIG_RT_HWCRYPTO_USING_AES_CTR=y
+# CONFIG_RT_HWCRYPTO_USING_AES_OFB is not set
+CONFIG_RT_HWCRYPTO_USING_DES=y
+CONFIG_RT_HWCRYPTO_USING_DES_ECB=y
+CONFIG_RT_HWCRYPTO_USING_DES_CBC=y
+CONFIG_RT_HWCRYPTO_USING_3DES=y
+CONFIG_RT_HWCRYPTO_USING_3DES_ECB=y
+CONFIG_RT_HWCRYPTO_USING_3DES_CBC=y
+CONFIG_RT_HWCRYPTO_USING_RC4=y
+# CONFIG_RT_HWCRYPTO_USING_MD5 is not set
+# CONFIG_RT_HWCRYPTO_USING_SHA1 is not set
+# CONFIG_RT_HWCRYPTO_USING_SHA2 is not set
+CONFIG_RT_HWCRYPTO_USING_RNG=y
+# CONFIG_RT_HWCRYPTO_USING_CRC is not set
+# CONFIG_RT_HWCRYPTO_USING_BIGNUM is not set
# CONFIG_RT_USING_WIFI is not set
# CONFIG_RT_USING_LED is not set
-# CONFIG_RT_USING_INPUT is not set
+CONFIG_RT_USING_INPUT=y
+CONFIG_RT_INPUT_POWER=y
+# CONFIG_RT_INPUT_UAPI is not set
+# CONFIG_RT_INPUT_JOYSTICK is not set
+CONFIG_RT_INPUT_KEYBOARD=y
+CONFIG_RT_INPUT_KEYBOARD_GPIO=y
+# CONFIG_RT_INPUT_MISC is not set
+# CONFIG_RT_INPUT_TOUCHSCREEN is not set
# CONFIG_RT_USING_MBOX is not set
# CONFIG_RT_USING_HWSPINLOCK is not set
# CONFIG_RT_USING_PHYE is not set
-# CONFIG_RT_USING_BLK is not set
-# CONFIG_RT_USING_SCSI is not set
-# CONFIG_RT_USING_FIRMWARE is not set
+CONFIG_RT_USING_ATA=y
+CONFIG_RT_ATA_AHCI=y
+CONFIG_RT_ATA_AHCI_PCI=y
+# CONFIG_RT_USING_NVME is not set
+CONFIG_RT_USING_BLK=y
+
+#
+# Partition Types
+#
+CONFIG_RT_BLK_PARTITION_DFS=y
+CONFIG_RT_BLK_PARTITION_EFI=y
+# end of Partition Types
+
+CONFIG_RT_USING_SCSI=y
+CONFIG_RT_SCSI_SD=y
+CONFIG_RT_SCSI_CDROM=y
+CONFIG_RT_USING_FIRMWARE=y
+CONFIG_RT_FIRMWARE_QEMU_FW_CFG=y
+# CONFIG_RT_FIRMWARE_ARM_SCMI is not set
# CONFIG_RT_USING_HWCACHE is not set
-# CONFIG_RT_USING_REGULATOR is not set
+CONFIG_RT_USING_REGULATOR=y
+# CONFIG_RT_REGULATOR_GPIO is not set
# CONFIG_RT_USING_RESET is not set
#
@@ -380,24 +429,50 @@ CONFIG_RT_USING_SOFT_RTC=y
# CONFIG_RT_USING_POWER_SUPPLY is not set
# CONFIG_RT_USING_THERMAL is not set
CONFIG_RT_USING_VIRTIO=y
-CONFIG_RT_USING_VIRTIO10=y
-CONFIG_RT_USING_VIRTIO_MMIO_ALIGN=y
-CONFIG_RT_USING_VIRTIO_BLK=y
-# CONFIG_RT_USING_VIRTIO_NET is not set
-CONFIG_RT_USING_VIRTIO_CONSOLE=y
-CONFIG_RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR=4
-CONFIG_RT_USING_VIRTIO_GPU=y
-CONFIG_RT_USING_VIRTIO_INPUT=y
+CONFIG_RT_VIRTIO_TRANSPORT_MMIO=y
+CONFIG_RT_VIRTIO_TRANSPORT_PCI=y
+CONFIG_RT_VIRTIO_BLK=y
+CONFIG_RT_VIRTIO_CONSOLE=y
+CONFIG_RT_VIRTIO_RNG=y
+CONFIG_RT_VIRTIO_SCSI=y
+# CONFIG_RT_VIRTIO_9P is not set
+CONFIG_RT_VIRTIO_RPROC_SERIAL=y
+CONFIG_RT_VIRTIO_GPU=y
+CONFIG_RT_VIRTIO_CRYPTO=y
# CONFIG_RT_USING_NVMEM is not set
-# CONFIG_RT_USING_DMA is not set
-# CONFIG_RT_USING_MFD is not set
+CONFIG_RT_USING_DMA=y
+CONFIG_RT_USING_MFD=y
+CONFIG_RT_MFD_EDU=y
+CONFIG_RT_MFD_SYSCON=y
CONFIG_RT_USING_OFW=y
# CONFIG_RT_USING_BUILTIN_FDT is not set
CONFIG_RT_FDT_EARLYCON_MSG_SIZE=128
CONFIG_RT_USING_OFW_BUS_RANGES_NUMBER=8
-# CONFIG_RT_USING_PIC is not set
+CONFIG_RT_USING_PCI=y
+CONFIG_RT_PCI_MSI=y
+# CONFIG_RT_PCI_ENDPOINT is not set
+CONFIG_RT_PCI_SYS_64BIT=y
+CONFIG_RT_PCI_CACHE_LINE_SIZE=8
+# CONFIG_RT_PCI_LOCKLESS is not set
+
+#
+# PCI Device Drivers
+#
+CONFIG_RT_PCI_ECAM=y
+CONFIG_RT_PCI_HOST_COMMON=y
+CONFIG_RT_PCI_HOST_GENERIC=y
+# CONFIG_RT_PCI_DW is not set
+CONFIG_RT_USING_PIC=y
+# CONFIG_RT_USING_PIC_STATISTICS is not set
+CONFIG_MAX_HANDLERS=512
+CONFIG_RT_PIC_ARM_GIC=y
+CONFIG_RT_PIC_ARM_GIC_V2M=y
+CONFIG_RT_PIC_ARM_GIC_V3=y
+CONFIG_RT_PIC_ARM_GIC_V3_ITS=y
+CONFIG_RT_PIC_ARM_GIC_V3_ITS_IRQ_MAX=127
+CONFIG_RT_PIC_ARM_GIC_MAX_NR=1
CONFIG_RT_USING_PIN=y
-# CONFIG_RT_PIN_PL061 is not set
+CONFIG_RT_PIN_PL061=y
# CONFIG_RT_USING_PINCTRL is not set
CONFIG_RT_USING_CLK=y
# CONFIG_RT_USING_CHERRYUSB is not set
@@ -1593,22 +1668,3 @@ CONFIG_RT_INIT_MEMORY_REGIONS=128
# end of RT-Thread online packages
CONFIG_SOC_VIRT64_AARCH64=y
-
-#
-# AARCH64 qemu virt64 configs
-#
-CONFIG_BSP_SUPPORT_FPU=y
-CONFIG_BSP_USING_UART=y
-CONFIG_RT_USING_UART0=y
-CONFIG_BSP_USING_RTC=y
-# CONFIG_BSP_USING_ALARM is not set
-CONFIG_BSP_USING_PIN=y
-CONFIG_BSP_USING_VIRTIO_BLK=y
-# CONFIG_BSP_USING_VIRTIO_NET is not set
-CONFIG_BSP_USING_VIRTIO_CONSOLE=y
-CONFIG_BSP_USING_VIRTIO_GPU=y
-CONFIG_BSP_USING_VIRTIO_INPUT=y
-CONFIG_BSP_USING_GIC=y
-CONFIG_BSP_USING_GICV2=y
-# CONFIG_BSP_USING_GICV3 is not set
-# end of AARCH64 qemu virt64 configs
diff --git a/bsp/qemu-virt64-aarch64/.gitignore b/bsp/qemu-virt64-aarch64/.gitignore
new file mode 100755
index 0000000000..eb6cf13dde
--- /dev/null
+++ b/bsp/qemu-virt64-aarch64/.gitignore
@@ -0,0 +1,2 @@
+*.qcow2
+*.dtb
diff --git a/bsp/qemu-virt64-aarch64/Kconfig b/bsp/qemu-virt64-aarch64/Kconfig
index 9a2d65614e..54597eb49e 100644
--- a/bsp/qemu-virt64-aarch64/Kconfig
+++ b/bsp/qemu-virt64-aarch64/Kconfig
@@ -6,20 +6,17 @@ RTT_DIR := ../../
PKGS_DIR := packages
+SOC_DM_SOC_DIR := $(BSP_DIR)/drivers
+
source "$(RTT_DIR)/Kconfig"
osource "$PKGS_DIR/Kconfig"
config SOC_VIRT64_AARCH64
bool
select ARCH_ARMV8
- select ARCH_CPU_64BIT
- select ARCH_ARM_MMU
- select RT_USING_CACHE
select RT_USING_COMPONENTS_INIT
select RT_USING_USER_MAIN
- select RT_USING_GIC
- select BSP_USING_GIC
- select ARCH_MM_MMU
+ select ARCH_CPU_64BIT
+ select RT_USING_CACHE
+ select RT_USING_STDC_ATOMIC
default y
-
-source "$(BSP_DIR)/drivers/Kconfig"
diff --git a/bsp/qemu-virt64-aarch64/amp.dts b/bsp/qemu-virt64-aarch64/amp.dts
new file mode 100755
index 0000000000..d01c5f81a2
--- /dev/null
+++ b/bsp/qemu-virt64-aarch64/amp.dts
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+/dts-v1/;
+#define AMP_SLAVE
+#include "amp.dtsi"
+#include "amp.dt.h"
+
+/delete-node/ &amp_memory;
+
+&pic_mailbox1 {
+ interrupt-affinity = <&cpu0>;
+};
+
+/ {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ model = "linux,dummy-virt";
+ interrupt-parent = <&gic>;
+
+ chosen {
+ bootargs = "rpmsg.mode=slave console=ttyAMA0 cma=8M coherent_pool=2M root=vda0 rootfstype=elm rw";
+ stdout-path = "/pl011@9040000";
+ };
+
+ memory {
+ reg = <0x0 (MEMORY_BASE + AMP_MEMORY_BASE) 0x0 AMP_MEMORY_SIZE>;
+ device_type = "memory";
+ };
+
+ cpus {
+ #size-cells = <0>;
+ #address-cells = <1>;
+
+ cpu0: cpu@CPU_ID {
+ compatible = ""; /* To be filled */
+ reg = <CPU_ID>;
+ device_type = "cpu";
+ };
+ };
+
+ timer {
+ compatible = "arm,armv8-timer","arm,armv7-timer";
+ always-on;
+ interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
+ <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
+ <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
+ <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
+ };
+
+ gic: interrupt-controller@8000000 {
+ #if GIC_VERSION != 2
+ compatible = "arm,gic-v3";
+ reg = <0x0 0x8000000 0x0 0x010000>, /* GICD */
+ <0x0 0x80a0000 0x0 0xf60000>; /* GICR */
+ #redistributor-regions = <0x01>;
+ #else
+ compatible = "arm,cortex-a15-gic";
+ reg = <0x0 0x8000000 0x0 0x010000>, /* GICD */
+ <0x0 0x8010000 0x0 0x010000>; /* GICC */
+ #endif
+ skip-init;
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ };
+
+ clk24m: apb-pclk {
+ clock-output-names = "clk24mhz";
+ clock-frequency = <24000000>;
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ };
+
+ pl011@9040000 {
+ clock-names = "uartclk", "apb_pclk";
+ clocks = <&clk24m &clk24m>;
+ interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-affinity = <&cpu0>;
+ reg = <0x00 0x9040000 0x00 0x1000>;
+ compatible = "arm,pl011", "arm,primecell";
+ };
+
+ virtio_blk {
+ compatible = "virtio,mmio";
+ reg = <0 0 0 0>; /* To be filled */
+ interrupts = <GIC_SPI 0 IRQ_TYPE_EDGE_RISING>; /* To be filled */
+ interrupt-affinity = <&cpu0>;
+ dma-coherent;
+ status = "disabled";
+ };
+
+ /delete-node/ soc;
+
+ amp_soc {
+ compatible = "simple-bus";
+ /delete-property/ memory-region;
+ /delete-property/ dtb-entry;
+ /delete-property/ kernel-entry;
+
+ virtio_rpmsg {
+ interrupt-affinity = <&cpu0>;
+ };
+ };
+};
diff --git a/bsp/qemu-virt64-aarch64/amp.dtsi b/bsp/qemu-virt64-aarch64/amp.dtsi
new file mode 100755
index 0000000000..2491d1b8a3
--- /dev/null
+++ b/bsp/qemu-virt64-aarch64/amp.dtsi
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#include <dt-bindings/size.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#define MEMORY_BASE 0x48000000
+#define MEMORY_SIZE (130 * SIZE_MB)
+
+#define AMP_MEMORY_BASE 0x200000
+#define AMP_MEMORY_SIZE (128 * SIZE_MB)
+
+/*
+ * RUN COMMAND: ./qemu.py -dtbo amp.dtsi -mem 258
+ * REMOTE: telnet localhost 4323
+ */
+
+/ {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges = <0x0 0x0 0x0 MEMORY_BASE 0x0 MEMORY_SIZE>;
+
+ pic_mailbox@1ffe00 {
+ reg = <0x0 0x1ffe00 0x0 0x200>;
+ };
+
+ virtio_sram: virtio_sram@0 {
+ reg = <0x0 0x0 0x0 0x1ffe00>;
+ };
+
+ amp_memory: amp_memory@200000 {
+ reg = <0x0 AMP_MEMORY_BASE 0x0 AMP_MEMORY_SIZE>;
+ };
+ };
+
+ soc {
+ compatible = "simple-bus";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges = <0x0 0x0 0x0 MEMORY_BASE 0x0 MEMORY_SIZE>;
+
+ pic_mailbox0: pic_mailbox@1ffe00 {
+ compatible = "rt-thread,pic-mailbox";
+ reg = <0x0 0x1ffe00 0x0 0x200>;
+ position = <0>;
+ interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ peer-interrupts = <(32 + 10)>;
+ uid = <0>;
+ #mbox-cells = <1>;
+ };
+
+ virtio_rpmsg {
+ compatible = "rt-thread,virtio-rpmsg";
+ shmem = <&virtio_sram>;
+ mboxes = <&pic_mailbox0 0>;
+ queue-max = <64>;
+ master;
+ };
+ };
+
+#ifndef AMP_SLAVE
+ /delete-node/ pl011@9040000;
+
+ aliases {
+ /delete-property/ serial1;
+ };
+#endif
+
+ amp_soc {
+ /* DO NOT set a 'compatible' property on this node */
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges = <0x0 0x0 0x0 MEMORY_BASE 0x0 MEMORY_SIZE>;
+ memory-region = <&amp_memory>;
+ dtb-entry = <0x48200000>;
+ kernel-entry = <0x48480000>;
+
+ pic_mailbox1: pic_mailbox@1ffe00 {
+ compatible = "rt-thread,pic-mailbox";
+ reg = <0x0 0x1ffe00 0x0 0x200>;
+ position = <1>;
+ interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
+ peer-interrupts = <(32 + 9)>;
+ uid = <1>;
+ #mbox-cells = <1>;
+ };
+
+ virtio_rpmsg {
+ compatible = "rt-thread,virtio-rpmsg";
+ shmem = <&virtio_sram>;
+ mboxes = <&pic_mailbox1 0>;
+ queue-max = <64>;
+ slave;
+ };
+ };
+};
diff --git a/bsp/qemu-virt64-aarch64/applications/SConscript b/bsp/qemu-virt64-aarch64/applications/SConscript
index c583d3016e..19c43aa9bf 100644
--- a/bsp/qemu-virt64-aarch64/applications/SConscript
+++ b/bsp/qemu-virt64-aarch64/applications/SConscript
@@ -4,6 +4,12 @@ cwd = GetCurrentDir()
src = Glob('*.c') + Glob('*.cpp')
CPPPATH = [cwd]
+if not GetDepend(['RT_RPMSG_RT_THREAD_VIRTIO']):
+ SrcRemove(src, ['amp.c'])
+
+if not GetDepend(['RT_SOC_CAMERA']):
+ SrcRemove(src, ['camera.c'])
+
group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
diff --git a/bsp/qemu-virt64-aarch64/applications/amp.c b/bsp/qemu-virt64-aarch64/applications/amp.c
new file mode 100755
index 0000000000..e9b35971e3
--- /dev/null
+++ b/bsp/qemu-virt64-aarch64/applications/amp.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <dt-bindings/size.h>
+
+#include <cpu.h>
+#include <mmu.h>
+#include <psci.h>
+#include <stdlib.h>
+#include <posix/stdio.h>
+
+#ifdef RT_USING_RPMSG
+static int rpmsg_write(int argc, char**argv)
+{
+ struct rt_device *dev;
+
+ if (argc != 3 || !(dev = rt_device_find(argv[1])))
+ {
+ rt_kprintf("Usage: %s <device> <message>\n", __func__);
+ return -1;
+ }
+
+ if (!rt_device_open(dev, 0))
+ {
+ rt_device_write(dev, 0, argv[2], rt_strlen(argv[2]) + 1);
+ rt_device_close(dev);
+ }
+ else
+ {
+ rt_kprintf("Open %s fail", argv[1]);
+ return -1;
+ }
+
+ return 0;
+}
+MSH_CMD_EXPORT(rpmsg_write, rpmsg write msg to device);
+
+static int rpmsg_read(int argc, char**argv)
+{
+ struct rt_device *dev;
+
+ if (argc != 2 || !(dev = rt_device_find(argv[1])))
+ {
+ rt_kprintf("Usage: %s <device>\n", __func__);
+ return -1;
+ }
+
+ if (!rt_device_open(dev, 0))
+ {
+ char msg[32];
+ rt_ssize_t len;
+
+ while ((len = rt_device_read(dev, 0, msg, sizeof(msg) - 1)) > 0)
+ {
+ msg[len] = '\0';
+ rt_kputs(msg);
+ }
+ rt_kputs("\n");
+
+ rt_device_close(dev);
+ }
+ else
+ {
+ rt_kprintf("Open %s fail", argv[1]);
+ return -1;
+ }
+
+ return 0;
+}
+MSH_CMD_EXPORT(rpmsg_read, rpmsg read all msg from device);
+
+static int rpmsg_startup(void)
+{
+ struct rt_rpmsg_endpoint_info info;
+ struct rt_device *dev = rt_device_find("rpmsg_char0");
+
+ if (dev && !rt_device_open(dev, 0))
+ {
+ rt_strcpy(info.name, "rpmsg-char");
+ info.src = 114;
+ info.dst = 514;
+ if (rt_rpmsg_mode() == RT_RPMSG_MODE_SLAVE)
+ {
+ info.src ^= info.dst;
+ info.dst ^= info.src;
+ info.src ^= info.dst;
+ }
+ rt_device_control(dev, RT_DEVICE_CTRL_RPMSG_CREATE_EPT, &info);
+
+ rt_device_close(dev);
+ }
+
+ return 0;
+}
+INIT_APP_EXPORT(rpmsg_startup);
+#endif /* RT_USING_RPMSG */
+
+struct compare
+{
+ struct rt_device *udev;
+ struct rt_device *vdev;
+};
+
+static int virtio_device_compare(rt_device_t dev, void *data)
+{
+ struct compare *cmp = data;
+
+ if (dev->user_data == (cmp->udev->user_data ? : cmp->udev))
+ {
+ cmp->vdev = dev;
+
+ return RT_EOK;
+ }
+
+ return -RT_EEMPTY;
+}
+
+static void join_virtio(void *amp_dtb, struct rt_bus *virtio_bus,
+ const char *dev_name, const char *path)
+{
+ int dev_offset;
+ rt_ssize_t len;
+ const void *data;
+ struct compare cmp;
+ struct rt_device *dev;
+ struct rt_ofw_node *np;
+
+ if (!(dev = rt_device_find(dev_name)))
+ {
+ return;
+ }
+
+ cmp.udev = dev;
+ cmp.vdev = RT_NULL;
+
+ rt_bus_for_each_dev(virtio_bus, &cmp, virtio_device_compare);
+
+ if (!cmp.vdev)
+ {
+ return;
+ }
+ np = cmp.vdev->ofw_node;
+ dev_offset = fdt_path_offset(amp_dtb, path);
+
+ data = rt_ofw_prop_read_raw(np, "reg", &len);
+ fdt_setprop(amp_dtb, dev_offset, "reg", data, len);
+
+ data = rt_ofw_prop_read_raw(np, "interrupts", &len);
+ fdt_setprop(amp_dtb, dev_offset, "interrupts", data, len);
+
+ fdt_setprop_cstring(amp_dtb, dev_offset, "status", "okay");
+
+ rt_bus_remove_device(cmp.vdev);
+}
+
+static void join_cpu(void *amp_dtb, struct rt_ofw_node *cpu_np)
+{
+ int cpu_offset;
+ const char *string = RT_NULL;
+
+ rt_ofw_prop_read_string(cpu_np, "compatible", &string);
+
+ fdt_open_into(amp_dtb, amp_dtb, fdt_totalsize(amp_dtb) + FDT_PADDING_SIZE);
+ cpu_offset = fdt_path_offset(amp_dtb, "/cpus/cpu@" RT_STRINGIFY(RT_CPUS_NR));
+ fdt_setprop_string(amp_dtb, cpu_offset, "compatible", string);
+}
+
+static rt_bool_t match_cpu_hwid(int cpu, rt_uint64_t hwid)
+{
+ return cpu == hwid;
+}
+
+static int amp_startup(void)
+{
+ rt_ubase_t level;
+ void *amp_kernel, *amp_dtb;
+ rt_uint32_t dtb_entry, amp_entry;
+ static rt_bool_t startup = RT_FALSE;
+ struct rt_bus *virtio_bus;
+ struct rt_ofw_node *amp_soc, *amp_mem, *amp_cpu;
+
+ if (startup || rt_ofw_bootargs_select("amp_mode", 0))
+ {
+ return -1;
+ }
+
+ startup = RT_TRUE;
+
+ if (!(amp_soc = rt_ofw_find_node_by_path("/amp_soc")))
+ {
+ rt_kprintf("No AMP SoC node found\n");
+ }
+
+ /* Build DTB */
+ rt_ofw_prop_read_u32(amp_soc, "dtb-entry", &dtb_entry);
+ amp_dtb = rt_ioremap_cached((void *)(rt_ubase_t)dtb_entry, ARCH_PAGE_SIZE);
+
+ if (!amp_dtb)
+ {
+ return (int)-RT_ENOMEM;
+ }
+
+ if (!(amp_mem = rt_ofw_parse_phandle(amp_soc, "memory-region", 0)))
+ {
+ rt_iounmap(amp_dtb);
+ return (int)-RT_EINVAL;
+ }
+
+ virtio_bus = rt_bus_find_by_name("virtio");
+ RT_ASSERT(virtio_bus != RT_NULL);
+
+ join_virtio(amp_dtb, virtio_bus, "vdb", "/virtio_blk");
+
+ /* Ready to start the AMP OS */
+ rt_ofw_prop_read_u32(amp_soc, "kernel-entry", &amp_entry);
+
+ /*
+ * Add ARM64 load DTB code (MOV <Wd>, #<imm>):
+ * sf [31]: 0b1
+ * opc [30-29]: 0b10
+ * code [28-23]: 0b100101
+ * hw [22-21]: 0b01 (MOVZ X0, #<dtb_entry >> 16>, LSL #16)
+ * imm16 [20-5]: dtb_entry >> 16
+ * Rd [4-0]: 0 (x0)
+ */
+ amp_entry -= sizeof(rt_uint32_t);
+ amp_kernel = rt_ioremap_cached((void *)(rt_ubase_t)amp_entry, ARCH_PAGE_SIZE);
+
+ if (!amp_kernel)
+ {
+ rt_iounmap(amp_dtb);
+ return (int)-RT_ENOMEM;
+ }
+ *(rt_uint32_t *)amp_kernel = RT_BIT(31);
+ *(rt_uint32_t *)amp_kernel |= 0x2 << 29;
+ *(rt_uint32_t *)amp_kernel |= 0x25 << 23;
+ *(rt_uint32_t *)amp_kernel |= 0x1 << 21;
+ *(rt_uint32_t *)amp_kernel |= (dtb_entry >> 16) << 5;
+ rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, amp_kernel, sizeof(rt_uint32_t));
+
+ rt_iounmap(amp_kernel);
+
+ level = rt_hw_interrupt_disable();
+ /*
+ * This is a hack, rt_cpu_mpidr_table array size is RT_CPUS_NR + 1,
+ * We can call PSCI_CPU_ON for the last one,
+ * We just restore it to zero after the PSCI called.
+ */
+ amp_cpu = rt_ofw_get_cpu_node(RT_CPUS_NR, RT_NULL, &match_cpu_hwid);
+ join_cpu(amp_dtb, amp_cpu);
+ rt_cpu_mpidr_table[RT_CPUS_NR] = rt_ofw_get_cpu_hwid(amp_cpu, 0);
+ rt_psci_cpu_on(RT_CPUS_NR, amp_entry);
+ rt_hw_rmb();
+
+ rt_cpu_mpidr_table[RT_CPUS_NR] = 0;
+ rt_hw_wmb();
+
+ rt_hw_interrupt_enable(level);
+
+ rt_iounmap(amp_dtb);
+
+ return 0;
+}
+#if defined(RT_USING_CONSOLE) && defined(RT_USING_MSH)
+MSH_CMD_EXPORT(amp_startup, AMP system startup);
+#else
+INIT_APP_EXPORT(amp_startup);
+#endif
diff --git a/bsp/qemu-virt64-aarch64/applications/camera.c b/bsp/qemu-virt64-aarch64/applications/camera.c
new file mode 100755
index 0000000000..b1593c461f
--- /dev/null
+++ b/bsp/qemu-virt64-aarch64/applications/camera.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2026-04-07 GuEe-GUI the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include <drv_camera.h>
+
+/* Pixel format conversion - inlined for performance */
+#define DO_CONV_RGB565(r, g, b, dst) \
+ do { *((rt_uint16_t*)(dst)) = (((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3); } while(0)
+
+#define DO_CONV_ARGB888(r, g, b, dst) \
+ do { *((rt_uint32_t*)(dst)) = (0xFF << 24) | ((r) << 16) | ((g) << 8) | (b); } while(0)
+
+#define DO_CONV_ABGR888(r, g, b, dst) \
+ do { *((rt_uint32_t*)(dst)) = (0xFF << 24) | ((b) << 16) | ((g) << 8) | (r); } while(0)
+
+/* Blend camera frame into framebuffer at specified position */
+static void blend_camera_to_fb(rt_uint8_t *cam_fb, rt_uint8_t *fb,
+ int cam_w, int cam_h,
+ int fb_w, int fb_h, int fb_pitch,
+ int dst_x, int dst_y,
+ int pixel_format)
+{
+ int x, y;
+ rt_uint8_t *dst, *src_row;
+ /* Calculate actual draw region (clip to framebuffer bounds) */
+ int draw_w = (dst_x + cam_w > fb_w) ? (fb_w - dst_x) : cam_w;
+ int draw_h = (dst_y + cam_h > fb_h) ? (fb_h - dst_y) : cam_h;
+
+ if (draw_w <= 0 || draw_h <= 0)
+ {
+ return;
+ }
+
+ /* Optimized conversion based on pixel format */
+ switch (pixel_format)
+ {
+ case RTGRAPHIC_PIXEL_FORMAT_RGB565:
+ {
+ rt_uint16_t *dst_pixel;
+ for (y = 0; y < draw_h; ++y)
+ {
+ src_row = cam_fb + y * cam_w * 3;
+ dst = fb + (dst_y + y) * fb_pitch + dst_x * 2;
+ dst_pixel = (rt_uint16_t*)dst;
+
+ for (x = 0; x < draw_w; ++x)
+ {
+ rt_uint8_t r = src_row[x * 3 + 0];
+ rt_uint8_t g = src_row[x * 3 + 1];
+ rt_uint8_t b = src_row[x * 3 + 2];
+ DO_CONV_RGB565(r, g, b, dst_pixel + x);
+ }
+ }
+ break;
+ }
+
+ case RTGRAPHIC_PIXEL_FORMAT_ARGB888:
+ {
+ rt_uint32_t *dst_pixel;
+ for (y = 0; y < draw_h; ++y)
+ {
+ src_row = cam_fb + y * cam_w * 3;
+ dst = fb + (dst_y + y) * fb_pitch + dst_x * 4;
+ dst_pixel = (rt_uint32_t*)dst;
+
+ for (x = 0; x < draw_w; ++x)
+ {
+ rt_uint8_t r = src_row[x * 3 + 0];
+ rt_uint8_t g = src_row[x * 3 + 1];
+ rt_uint8_t b = src_row[x * 3 + 2];
+ DO_CONV_ARGB888(r, g, b, dst_pixel + x);
+ }
+ }
+ break;
+ }
+
+ case RTGRAPHIC_PIXEL_FORMAT_ABGR888:
+ {
+ rt_uint32_t *dst_pixel;
+ for (y = 0; y < draw_h; ++y)
+ {
+ src_row = cam_fb + y * cam_w * 3;
+ dst = fb + (dst_y + y) * fb_pitch + dst_x * 4;
+ dst_pixel = (rt_uint32_t*)dst;
+
+ for (x = 0; x < draw_w; ++x)
+ {
+ rt_uint8_t r = src_row[x * 3 + 0];
+ rt_uint8_t g = src_row[x * 3 + 1];
+ rt_uint8_t b = src_row[x * 3 + 2];
+ DO_CONV_ABGR888(r, g, b, dst_pixel + x);
+ }
+ }
+ break;
+ }
+
+ default:
+ /* Fallback: direct copy for RGB888 */
+ for (y = 0; y < draw_h; ++y)
+ {
+ src_row = cam_fb + y * cam_w * 3;
+ dst = fb + (dst_y + y) * fb_pitch + dst_x * 3;
+ rt_memcpy(dst, src_row, draw_w * 3);
+ }
+ break;
+ }
+}
+
+static int camera(int argc, char**argv)
+{
+ rt_err_t err;
+ struct rt_device *cdev, *gdev;
+ struct rt_device_graphic_info info;
+ int cam_w, cam_h;
+ void *cam_fb;
+ int dst_x, dst_y;
+
+ cdev = rt_device_find(argc > 1 ? argv[1] : "camera0");
+ if (!cdev)
+ {
+ rt_kputs("Camera device not found\n");
+ return -1;
+ }
+
+ gdev = rt_device_find(argc > 2 ? argv[2] : "fb0");
+ if (!gdev)
+ {
+ rt_kputs("Framebuffer device not found\n");
+ return -1;
+ }
+
+ if ((err = rt_device_open(cdev, 0)))
+ {
+ rt_kprintf("Open camera failed: %d\n", err);
+ return (int)err;
+ }
+
+ if ((err = rt_device_open(gdev, 0)))
+ {
+ rt_kprintf("Open framebuffer failed: %d\n", err);
+ goto _close_camera;
+ }
+
+ if ((err = rt_device_control(gdev, RTGRAPHIC_CTRL_GET_INFO, &info)))
+ {
+ rt_kprintf("Get framebuffer info failed: %d\n", err);
+ goto _close_fb;
+ }
+
+ /* Get camera resolution */
+ rt_device_control(cdev, RT_DEVICE_CTRL_CAMERA_GET_WIDTH, &cam_w);
+ rt_device_control(cdev, RT_DEVICE_CTRL_CAMERA_GET_HEIGHT, &cam_h);
+
+ /* Check pixel format support */
+ if (info.pixel_format != RTGRAPHIC_PIXEL_FORMAT_RGB565 &&
+ info.pixel_format != RTGRAPHIC_PIXEL_FORMAT_ARGB888 &&
+ info.pixel_format != RTGRAPHIC_PIXEL_FORMAT_ABGR888 &&
+ info.pixel_format != RTGRAPHIC_PIXEL_FORMAT_RGB888)
+ {
+ rt_kprintf("Unsupported pixel format: %d\n", info.pixel_format);
+ goto _close_fb;
+ }
+
+ /* Get direct framebuffer access */
+ rt_device_control(cdev, RT_DEVICE_CTRL_CAMERA_GET_FRAMEBUFFER, &cam_fb);
+
+ /* Center camera on framebuffer */
+ dst_x = (info.width - cam_w) / 2;
+ dst_y = (info.height - cam_h) / 2;
+ if (dst_x < 0) dst_x = 0;
+ if (dst_y < 0) dst_y = 0;
+
+ /* Main loop - read and display frames: ~10 seconds at 30 FPS */
+ for (int frame_count = 0; frame_count < 300;)
+ {
+ /* Wait for new frame (short timeout for low latency) */
+ if (rt_device_read(cdev, 0, RT_NULL, 0) > 0)
+ {
+ /* Blend camera into framebuffer */
+ blend_camera_to_fb(cam_fb, info.framebuffer,
+ cam_w, cam_h,
+ info.width, info.height, info.pitch,
+ dst_x, dst_y,
+ info.pixel_format);
+ ++frame_count;
+ }
+ }
+
+_close_fb:
+ rt_device_close(gdev);
+
+_close_camera:
+ rt_device_close(cdev);
+
+ return err;
+}
+MSH_CMD_EXPORT(camera, camera show in framebuffer);
diff --git a/bsp/qemu-virt64-aarch64/applications/console.c b/bsp/qemu-virt64-aarch64/applications/console.c
deleted file mode 100644
index 3f8be48189..0000000000
--- a/bsp/qemu-virt64-aarch64/applications/console.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2021-11-11 GuEe-GUI the first version
- */
-
-#include <rtthread.h>
-
-#include <virtio_console.h>
-
-static int console_init()
-{
- rt_err_t status = RT_EOK;
- rt_device_t device = rt_device_find("virtio-console0");
-
- if (device != RT_NULL && rt_device_open(device, 0) == RT_EOK)
- {
- /* Create vport0p1 */
- status = rt_device_control(device, VIRTIO_DEVICE_CTRL_CONSOLE_PORT_CREATE, RT_NULL);
- }
-
- if (device != RT_NULL)
- {
- rt_device_close(device);
- }
-
- return status;
-}
-INIT_ENV_EXPORT(console_init);
diff --git a/bsp/qemu-virt64-aarch64/applications/graphic.c b/bsp/qemu-virt64-aarch64/applications/graphic.c
deleted file mode 100644
index c54132bb73..0000000000
--- a/bsp/qemu-virt64-aarch64/applications/graphic.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2021-11-11 GuEe-GUI the first version
- */
-
-#include <rtthread.h>
-#include <rtdevice.h>
-
-#include <virtio_gpu.h>
-#include <virtio_input.h>
-
-#define GRAPHIC_THREAD_PRIORITY 25
-#define GRAPHIC_THREAD_STACK_SIZE 4096
-#define GRAPHIC_THREAD_TIMESLICE 5
-
-static rt_uint32_t cur_min[2];
-static rt_uint32_t cur_max[2];
-static rt_uint32_t cur_range[2];
-static rt_uint32_t cur_points[2];
-static rt_uint32_t cur_last_points[2];
-static rt_bool_t cur_event_sync;
-static rt_uint32_t color[2] = { 0xff0000, 0x0000ff };
-static rt_uint8_t cursor[VIRTIO_GPU_CURSOR_IMG_SIZE] rt_align(VIRTIO_PAGE_SIZE);
-
-void tablet_event_handler(struct virtio_input_event event)
-{
- static rt_bool_t cur_btn_down = RT_FALSE;
-
- if (event.type == EV_ABS)
- {
- if (event.code == 0)
- {
- cur_points[0] = (cur_max[0] * (event.value - cur_min[0]) + cur_range[0] / 2) / cur_range[0];
- }
- else if (event.code == 1)
- {
- cur_points[1] = (cur_max[1] * (event.value - cur_min[1]) + cur_range[1] / 2) / cur_range[1];
- }
- }
- else if (event.type == EV_KEY)
- {
- if (event.code == BTN_LEFT)
- {
- if (cur_btn_down && event.value == 0)
- {
- color[0] ^= color[1];
- color[1] ^= color[0];
- color[0] ^= color[1];
- cur_btn_down = RT_FALSE;
- cur_event_sync = RT_TRUE;
- }
- else
- {
- cur_btn_down = RT_TRUE;
- }
- }
- }
- else if (event.type == EV_SYN)
- {
- cur_event_sync = RT_TRUE;
- }
-}
-
-void graphic_thread(void *param)
-{
- int i;
- char dev_name[RT_NAME_MAX];
- rt_device_t device = RT_NULL;
-
- rt_device_t tablet_dev = RT_NULL;
- struct virtio_input_config tablet_config;
-
- rt_uint32_t white = 0xffffff;
- rt_device_t gpu_dev = RT_NULL;
- struct rt_device_rect_info rect_info;
- struct rt_device_graphic_info graphic_info;
- struct rt_device_graphic_ops *virtio_gpu_graphic_ops;
-
- /* GPU */
- device = rt_device_find("virtio-gpu0");
-
- if (device != RT_NULL && rt_device_open(device, 0) == RT_EOK)
- {
- virtio_gpu_graphic_ops = rt_graphix_ops(device);
-
- rt_memset(&rect_info, 0, sizeof(rect_info));
- rt_memset(&graphic_info, 0, sizeof(graphic_info));
-
- rt_device_control(device, VIRTIO_DEVICE_CTRL_GPU_SET_PRIMARY, RT_NULL);
- rt_device_control(device, VIRTIO_DEVICE_CTRL_GPU_CREATE_2D, (void *)RTGRAPHIC_PIXEL_FORMAT_RGB888);
- rt_device_control(device, RTGRAPHIC_CTRL_GET_INFO, &graphic_info);
-
- rect_info.x = 0;
- rect_info.y = 0;
- rect_info.width = graphic_info.width;
- rect_info.height = graphic_info.height;
-
- if (graphic_info.framebuffer != RT_NULL)
- {
- int i = 0;
-
- rt_memset(graphic_info.framebuffer, 0xff, graphic_info.pitch * graphic_info.height);
-
- cur_last_points[0] = graphic_info.width / 2;
- cur_last_points[1] = graphic_info.height / 2;
-
- virtio_gpu_graphic_ops->draw_hline((char *)&color[0], 0, graphic_info.width, cur_last_points[1]);
- virtio_gpu_graphic_ops->draw_vline((char *)&color[1], cur_last_points[0], 0, graphic_info.height);
-
- rt_device_control(device, RTGRAPHIC_CTRL_RECT_UPDATE, &rect_info);
-
- while (i < sizeof(cursor) / 4)
- {
- /* R: 0x4c G: 0xaf B: 0x50 A: 0.8 */
- ((rt_uint32_t *)cursor)[i] = 0xcc4caf50;
- ++i;
- }
-
- rt_device_control(device, VIRTIO_DEVICE_CTRL_CURSOR_SETUP, cursor);
- rt_device_control(device, VIRTIO_DEVICE_CTRL_CURSOR_MOVE, (rt_uint32_t[]){0, 0});
-
- gpu_dev = device;
- }
- }
-
- /* Keyboard, Mouse, Tablet */
- for (i = 0; i < 3; ++i)
- {
- rt_snprintf(dev_name, RT_NAME_MAX, "virtio-input%d", i);
-
- device = rt_device_find(dev_name);
-
- if (device != RT_NULL && rt_device_open(device, 0) == RT_EOK)
- {
- enum virtio_input_type type;
- rt_device_control(device, VIRTIO_DEVICE_CTRL_INPUT_GET_TYPE, &type);
-
- if (type == VIRTIO_INPUT_TYPE_TABLET)
- {
- tablet_dev = device;
- }
- else
- {
- rt_device_close(device);
- }
- }
- }
-
- if (tablet_dev == RT_NULL || gpu_dev == RT_NULL)
- {
- goto _graphic_fail;
- }
-
- cur_max[0] = graphic_info.width;
- cur_max[1] = graphic_info.height;
-
- rt_device_control(tablet_dev, VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_X_INFO, &tablet_config);
-
- cur_min[0] = tablet_config.abs.min;
- cur_range[0] = tablet_config.abs.max - cur_min[0];
-
- rt_device_control(tablet_dev, VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_Y_INFO, &tablet_config);
-
- cur_min[1] = tablet_config.abs.min;
- cur_range[1] = tablet_config.abs.max - cur_min[1];
-
- cur_event_sync = RT_FALSE;
-
- rt_device_control(tablet_dev, VIRTIO_DEVICE_CTRL_INPUT_BIND_BSCT_HANDLER, tablet_event_handler);
-
- for (;;)
- {
- while (cur_event_sync)
- {
- virtio_gpu_graphic_ops->draw_hline((char *)&white, 0, graphic_info.width, cur_last_points[1]);
- virtio_gpu_graphic_ops->draw_vline((char *)&white, cur_last_points[0], 0, graphic_info.height);
-
- cur_last_points[0] = cur_points[0];
- cur_last_points[1] = cur_points[1];
-
- virtio_gpu_graphic_ops->draw_hline((char *)&color[0], 0, graphic_info.width, cur_last_points[1]);
- virtio_gpu_graphic_ops->draw_vline((char *)&color[1], cur_last_points[0], 0, graphic_info.height);
-
- rt_device_control(gpu_dev, RTGRAPHIC_CTRL_RECT_UPDATE, &rect_info);
-
- cur_event_sync = RT_FALSE;
-
- rt_thread_mdelay(1);
- }
- }
-
-_graphic_fail:
-
- if (gpu_dev != RT_NULL)
- {
- rt_device_close(gpu_dev);
- }
-
- if (tablet_dev != RT_NULL)
- {
- rt_device_close(tablet_dev);
- }
-}
-
-int graphic_test(void)
-{
- rt_thread_t graphic_tid = rt_thread_create("graphic work", graphic_thread, RT_NULL,
- GRAPHIC_THREAD_STACK_SIZE, GRAPHIC_THREAD_PRIORITY, GRAPHIC_THREAD_TIMESLICE);
-
- if (graphic_tid != RT_NULL)
- {
- rt_thread_startup(graphic_tid);
-
- return RT_EOK;
- }
-
- return -RT_ERROR;
-}
-MSH_CMD_EXPORT(graphic_test, Graphic test);
diff --git a/bsp/qemu-virt64-aarch64/applications/mnt.c b/bsp/qemu-virt64-aarch64/applications/mnt.c
deleted file mode 100644
index 262c19c4f7..0000000000
--- a/bsp/qemu-virt64-aarch64/applications/mnt.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2021/08/19 bernard the first version
- */
-
-#include <rtthread.h>
-
-#ifdef RT_USING_DFS
-#include <dfs_fs.h>
-
-int mnt_init(void)
-{
- if (rt_device_find("virtio-blk0"))
- {
- /* mount virtio-blk as root directory */
- if (dfs_mount("virtio-blk0", "/", "elm", 0, RT_NULL) == 0)
- {
- rt_kprintf("file system initialization done!\n");
- }
- else
- {
- if (dfs_mount("virtio-blk0", "/", "ext", 0, RT_NULL) == 0)
- {
- rt_kprintf("file system initialization done!\n");
- }
- else
- {
- rt_kprintf("file system initialization fail!\n");
- }
- }
- }
-
- return 0;
-}
-INIT_ENV_EXPORT(mnt_init);
-#endif
diff --git a/bsp/qemu-virt64-aarch64/applications/pin.c b/bsp/qemu-virt64-aarch64/applications/pin.c
deleted file mode 100644
index 37d1927b00..0000000000
--- a/bsp/qemu-virt64-aarch64/applications/pin.c
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (c) 2006-2022, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2022-6-30 GuEe-GUI first version
- */
-
-#include <rthw.h>
-#include <rtthread.h>
-#include <rtdevice.h>
-
-#ifdef RT_USING_PIN
-
-void qemu_gpio3_key_poweroff(void *args)
-{
- rt_kprintf("\nYou power off the machine.\n");
-
- rt_hw_cpu_shutdown();
-}
-
-static int pin_init()
-{
- rt_pin_attach_irq(3, PIN_IRQ_MODE_FALLING, qemu_gpio3_key_poweroff, RT_NULL);
- rt_pin_irq_enable(3, RT_TRUE);
-
- return 0;
-}
-INIT_ENV_EXPORT(pin_init);
-
-#endif /* RT_USING_PIN */
diff --git a/bsp/qemu-virt64-aarch64/drivers/Kconfig b/bsp/qemu-virt64-aarch64/drivers/Kconfig
index f14fc73fc1..d42783b16c 100644..100755
--- a/bsp/qemu-virt64-aarch64/drivers/Kconfig
+++ b/bsp/qemu-virt64-aarch64/drivers/Kconfig
@@ -1,80 +1,15 @@
+config RT_GRAPHIC_ROMFB_WIDTH
+ int "ROM FB width"
+ depends on RT_GRAPHIC_FB
+ default 800
-menu "AARCH64 qemu virt64 configs"
- menuconfig BSP_SUPPORT_FPU
- bool "Using Float"
- default y
+config RT_GRAPHIC_ROMFB_HEIGHT
+ int "ROM FB height"
+ depends on RT_GRAPHIC_FB
+ default 600
- menuconfig BSP_USING_UART
- bool "Using UART"
- select RT_USING_SERIAL
- default y
-
- if BSP_USING_UART
- config RT_USING_UART0
- bool "Enabel UART 0"
- default y
- endif
-
- menuconfig BSP_USING_RTC
- bool "Using RTC"
- select RT_USING_RTC
- default y
-
- if BSP_USING_RTC
- config BSP_USING_ALARM
- bool "Enable Alarm"
- select RT_USING_ALARM
- default n
- endif
-
- config BSP_USING_PIN
- bool "Using PIN"
- select RT_USING_PIN
- default y
-
- config BSP_USING_VIRTIO_BLK
- bool "Using VirtIO BLK"
- select RT_USING_VIRTIO
- select RT_USING_VIRTIO_BLK
- default y
-
- config BSP_USING_VIRTIO_NET
- bool "Using VirtIO NET"
- select RT_USING_VIRTIO
- select RT_USING_VIRTIO_NET
- default y
-
- config BSP_USING_VIRTIO_CONSOLE
- bool "Using VirtIO Console"
- select RT_USING_VIRTIO
- select RT_USING_VIRTIO_CONSOLE
- default y
-
- config BSP_USING_VIRTIO_GPU
- bool "Using VirtIO GPU"
- select RT_USING_VIRTIO
- select RT_USING_VIRTIO_GPU
- default y
-
- config BSP_USING_VIRTIO_INPUT
- bool "Using VirtIO Input"
- select RT_USING_VIRTIO
- select RT_USING_VIRTIO_INPUT
- default y
-
- config BSP_USING_GIC
- bool
- default y
-
- choice
- prompt "GIC Version"
- default BSP_USING_GICV2
-
- config BSP_USING_GICV2
- bool "GICv2"
-
- config BSP_USING_GICV3
- bool "GICv3"
- endchoice
-
-endmenu
+config RT_SOC_CAMERA
+ bool "Camera"
+ depends on RT_GRAPHIC_FB
+ depends on RT_USING_PCI
+ default y \ No newline at end of file
diff --git a/bsp/qemu-virt64-aarch64/drivers/SConscript b/bsp/qemu-virt64-aarch64/drivers/SConscript
index 56a5855113..13f67c1e68 100644..100755
--- a/bsp/qemu-virt64-aarch64/drivers/SConscript
+++ b/bsp/qemu-virt64-aarch64/drivers/SConscript
@@ -3,9 +3,15 @@
from building import *
cwd = GetCurrentDir()
-src = Glob('*.c')
+src = ['board.c']
CPPPATH = [cwd]
+if GetDepend(['RT_GRAPHIC_FB']):
+ src += ['drv_romfb.c']
+
+if GetDepend(['RT_SOC_CAMERA']):
+ src += ['drv_camera.c']
+
group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
diff --git a/bsp/qemu-virt64-aarch64/drivers/board.c b/bsp/qemu-virt64-aarch64/drivers/board.c
index 3bccfad195..489eb0b99e 100644
--- a/bsp/qemu-virt64-aarch64/drivers/board.c
+++ b/bsp/qemu-virt64-aarch64/drivers/board.c
@@ -11,14 +11,8 @@
* add smp ipi init
*/
-#include <mmu.h>
-#include <board.h>
-#include <mm_aspace.h>
-#include <mm_page.h>
#include <setup.h>
-extern size_t MMUTable[];
-
void rt_hw_board_init(void)
{
rt_hw_common_setup();
diff --git a/bsp/qemu-virt64-aarch64/drivers/board.h b/bsp/qemu-virt64-aarch64/drivers/board.h
index e9fc17a283..23148a28a5 100644
--- a/bsp/qemu-virt64-aarch64/drivers/board.h
+++ b/bsp/qemu-virt64-aarch64/drivers/board.h
@@ -12,16 +12,9 @@
#ifndef __BOARD_H__
#define __BOARD_H__
-#include <virt.h>
-
extern unsigned char __bss_start;
extern unsigned char __bss_end;
-#define HEAP_BEGIN (void *)&__bss_end
-#define HEAP_END ((void *)HEAP_BEGIN + 64 * 1024 * 1024)
-
void rt_hw_board_init(void);
-int rt_hw_uart_init(void);
-
-#endif
+#endif /* __BOARD_H__ */
diff --git a/bsp/qemu-virt64-aarch64/drivers/drv_camera.c b/bsp/qemu-virt64-aarch64/drivers/drv_camera.c
new file mode 100755
index 0000000000..37fd05d804
--- /dev/null
+++ b/bsp/qemu-virt64-aarch64/drivers/drv_camera.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2026-04-07 GuEe-GUI the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define DBG_TAG "vfio.camera"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include <drv_camera.h>
+
+#define VFIO_CAMERA_WIDTH_BAR 0
+#define VFIO_CAMERA_HEIGHT_BAR 1
+#define VFIO_CAMERA_FRAMEBUFFER_BAR 2
+#define VFIO_CAMERA_STATUS_BAR 3
+
+/* Status register */
+#define BAR3_FRAME_COUNT_OFFSET 0x00
+#define BAR3_FRAME_READY_OFFSET 0x04
+
+struct vfio_camera
+{
+ struct rt_device parent;
+ void *framebuffer;
+ void *status_regs; /* BAR3: status registers */
+ int width;
+ int height;
+ rt_uint32_t last_frame_count;
+ struct rt_semaphore frame_sem; /* Frame ready notification */
+};
+
+static rt_uint32_t vfio_camera_get_frame_count(struct vfio_camera *vc)
+{
+ return HWREG32(vc->status_regs + BAR3_FRAME_COUNT_OFFSET);
+}
+
+static rt_bool_t vfio_camera_wait_frame(struct vfio_camera *vc, rt_int32_t timeout_ms)
+{
+ rt_uint32_t current_count;
+ rt_tick_t start_tick, timeout_tick;
+
+ /* Fast path: check frame count first */
+ current_count = vfio_camera_get_frame_count(vc);
+ if (current_count != vc->last_frame_count)
+ {
+ vc->last_frame_count = current_count;
+ return RT_TRUE;
+ }
+
+ start_tick = rt_tick_get();
+ timeout_tick = rt_tick_from_millisecond(timeout_ms);
+
+ /* Poll with small sleep - allow frame dropping */
+ do {
+ current_count = vfio_camera_get_frame_count(vc);
+
+ if (current_count != vc->last_frame_count)
+ {
+ vc->last_frame_count = current_count;
+ return RT_TRUE;
+ }
+
+ /* Short sleep to reduce CPU usage */
+ rt_thread_mdelay(1);
+
+ /* Check overall timeout */
+ if ((rt_tick_get() - start_tick) >= timeout_tick)
+ {
+ /* Timeout */
+ return RT_FALSE;
+ }
+ } while (RT_TRUE);
+}
+
+static rt_ssize_t vfio_camera_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+ struct vfio_camera *vc = rt_container_of(dev, struct vfio_camera, parent);
+
+ /* Wait for new frame (up to 30ms - about 1 frame at 30fps) */
+ if (!vfio_camera_wait_frame(vc, 30))
+ {
+ return 0;
+ }
+
+ /* If buffer is NULL, just return success (frame is ready) */
+ if (buffer == RT_NULL)
+ {
+ return 1; /* Frame ready */
+ }
+
+ /* Direct memory access - no memcpy overhead */
+ rt_memcpy(buffer, vc->framebuffer + pos, size);
+ return size;
+}
+
+static rt_err_t vfio_camera_control(rt_device_t dev, int cmd, void *args)
+{
+ struct vfio_camera *vc = rt_container_of(dev, struct vfio_camera, parent);
+
+ switch (cmd)
+ {
+ case RT_DEVICE_CTRL_CAMERA_GET_WIDTH:
+ if (args)
+ {
+ *(int *)args = vc->width;
+ }
+ break;
+
+ case RT_DEVICE_CTRL_CAMERA_GET_HEIGHT:
+ if (args)
+ {
+ *(int *)args = vc->height;
+ }
+ break;
+
+ case RT_DEVICE_CTRL_CAMERA_GET_FRAMEBUFFER:
+ if (args)
+ {
+ *(void **)args = vc->framebuffer;
+ }
+ break;
+
+ default:
+ return -RT_EINVAL;
+ }
+
+ return RT_EOK;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops vfio_camera_ops =
+{
+ .read = vfio_camera_read,
+ .control = vfio_camera_control,
+};
+#endif
+
+static void vfio_camera_isr(int irqno, void *args)
+{
+ struct vfio_camera *vc = args;
+
+ /* Frame ready interrupt from backend */
+ rt_sem_release(&vc->frame_sem);
+}
+
+static rt_err_t vfio_camera_probe(struct rt_pci_device *pdev)
+{
+ rt_err_t err;
+ void *fmt_regs;
+ const char *dev_name;
+ struct vfio_camera *vc = rt_calloc(1, sizeof(*vc));
+
+ if (!vc)
+ {
+ LOG_E("Failed to allocate vfio_camera");
+ return -RT_ENOMEM;
+ }
+
+ /* Initialize semaphore for frame notification */
+ rt_sem_init(&vc->frame_sem, "cam_frm", 0, RT_IPC_FLAG_PRIO);
+
+ fmt_regs = rt_pci_iomap(pdev, VFIO_CAMERA_WIDTH_BAR);
+ if (!fmt_regs)
+ {
+ LOG_E("Failed to map BAR0 (width)");
+ goto _fail;
+ }
+ vc->width = HWREG32(fmt_regs);
+ rt_iounmap(fmt_regs);
+
+ fmt_regs = rt_pci_iomap(pdev, VFIO_CAMERA_HEIGHT_BAR);
+ if (!fmt_regs)
+ {
+ LOG_E("Failed to map BAR1 (height)");
+ goto _fail;
+ }
+ vc->height = HWREG32(fmt_regs);
+ rt_iounmap(fmt_regs);
+
+ vc->framebuffer = rt_pci_iomap(pdev, VFIO_CAMERA_FRAMEBUFFER_BAR);
+ if (!vc->framebuffer)
+ {
+ LOG_E("Failed to map BAR2 (framebuffer)");
+ goto _fail;
+ }
+
+ vc->status_regs = rt_pci_iomap(pdev, VFIO_CAMERA_STATUS_BAR);
+ if (!vc->status_regs)
+ {
+ LOG_E("Failed to map BAR3 (status)");
+ goto _fail;
+ }
+
+ /* Initialize frame count */
+ vc->last_frame_count = vfio_camera_get_frame_count(vc);
+
+ vc->parent.type = RT_Device_Class_Miscellaneous;
+#ifdef RT_USING_DEVICE_OPS
+ vc->parent.ops = &vfio_camera_ops;
+#else
+ vc->parent.read = vfio_camera_read;
+ vc->parent.control = vfio_camera_control;
+#endif
+
+ rt_dm_dev_set_name_auto(&vc->parent, "camera");
+ dev_name = rt_dm_dev_get_name(&vc->parent);
+
+ if ((err = rt_device_register(&vc->parent, dev_name, RT_DEVICE_FLAG_RDONLY)))
+ {
+ LOG_E("Failed to register device: %d", err);
+ goto _fail;
+ }
+
+ rt_hw_interrupt_install(pdev->irq, vfio_camera_isr, vc, dev_name);
+ rt_hw_interrupt_umask(pdev->irq);
+ rt_pci_irq_unmask(pdev);
+
+ pdev->parent.user_data = vc;
+
+ LOG_I("%s %ux%u %s", dev_name, vc->width, vc->height, "RGB24");
+
+ return RT_EOK;
+
+_fail:
+ if (vc->status_regs)
+ {
+ rt_iounmap(vc->status_regs);
+ }
+ if (vc->framebuffer)
+ {
+ rt_iounmap(vc->framebuffer);
+ }
+ rt_sem_detach(&vc->frame_sem);
+ rt_free(vc);
+
+ return err;
+}
+
+static rt_err_t vfio_camera_remove(struct rt_pci_device *pdev)
+{
+ struct vfio_camera *vc = pdev->parent.user_data;
+
+ /* INTx is shared, don't mask all */
+ rt_hw_interrupt_umask(pdev->irq);
+ rt_pci_irq_mask(pdev);
+ rt_pci_clear_master(pdev);
+
+ rt_device_unregister(&vc->parent);
+
+ rt_iounmap(vc->status_regs);
+ rt_iounmap(vc->framebuffer);
+ rt_sem_detach(&vc->frame_sem);
+ rt_free(vc);
+
+ return RT_EOK;
+}
+
+static const struct rt_pci_device_id vfio_camera_pci_ids[] =
+{
+ { RT_PCI_DEVICE_ID(PCI_VENDOR_ID_QEMU, 0x5678), },
+ { /* sentinel */ }
+};
+
+static struct rt_pci_driver vfio_camera_driver =
+{
+ .name = "vfio-camera",
+
+ .ids = vfio_camera_pci_ids,
+ .probe = vfio_camera_probe,
+ .remove = vfio_camera_remove,
+};
+RT_PCI_DRIVER_EXPORT(vfio_camera_driver);
diff --git a/bsp/qemu-virt64-aarch64/drivers/drv_camera.h b/bsp/qemu-virt64-aarch64/drivers/drv_camera.h
new file mode 100755
index 0000000000..a9c03aeb6d
--- /dev/null
+++ b/bsp/qemu-virt64-aarch64/drivers/drv_camera.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2026-04-07 GuEe-GUI the first version
+ */
+
+#ifndef __DRV_CAMERA_H__
+#define __DRV_CAMERA_H__
+
+#include <rthw.h>
+#include <rtthread.h>
+
+#define RT_DEVICE_CTRL_CAMERA_GET_WIDTH (RT_DEVICE_CTRL_BASE(Char) + 'V' + 1)
+#define RT_DEVICE_CTRL_CAMERA_GET_HEIGHT (RT_DEVICE_CTRL_BASE(Char) + 'V' + 2)
+#define RT_DEVICE_CTRL_CAMERA_GET_FRAMEBUFFER (RT_DEVICE_CTRL_BASE(Char) + 'V' + 3)
+
+#endif /* __DRV_CAMERA_H__ */
diff --git a/bsp/qemu-virt64-aarch64/drivers/drv_gpio.c b/bsp/qemu-virt64-aarch64/drivers/drv_gpio.c
deleted file mode 100644
index bcf6677d2c..0000000000
--- a/bsp/qemu-virt64-aarch64/drivers/drv_gpio.c
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * Copyright (c) 2006-2022, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2022-6-30 GuEe-GUI first version
- */
-
-#include <rthw.h>
-#include <rtthread.h>
-#include <rtdevice.h>
-#include <board.h>
-
-#include "drv_gpio.h"
-
-#ifdef BSP_USING_PIN
-
-#define GPIODIR 0x400
-#define GPIOIS 0x404
-#define GPIOIBE 0x408
-#define GPIOIEV 0x40c
-#define GPIOIE 0x410
-#define GPIORIS 0x414
-#define GPIOMIS 0x418
-#define GPIOIC 0x41c
-
-#define BIT(x) (1UL << (x))
-
-#define PL061_GPIO_NR 8
-
-static struct pl061
-{
-#ifdef RT_USING_SMP
- struct rt_spinlock spinlock;
-#endif
- void (*(hdr[PL061_GPIO_NR]))(void *args);
- void *args[PL061_GPIO_NR];
-} _pl061;
-
-static rt_ubase_t pl061_gpio_base = PL061_GPIO_BASE;
-
-rt_inline rt_uint8_t pl061_read8(rt_ubase_t offset)
-{
- return HWREG8(pl061_gpio_base + offset);
-}
-
-rt_inline void pl061_write8(rt_ubase_t offset, rt_uint8_t value)
-{
- HWREG8(pl061_gpio_base + offset) = value;
-}
-
-static void pl061_pin_mode(struct rt_device *device, rt_base_t pin, rt_uint8_t mode)
-{
- int value;
- rt_uint8_t gpiodir;
-
-#ifdef RT_USING_SMP
- rt_base_t level;
-#endif
-
- if (pin < 0 || pin >= PL061_GPIO_NR)
- {
- return;
- }
-
-#ifdef RT_USING_SMP
- level = rt_spin_lock_irqsave(&_pl061.spinlock);
-#endif
-
- switch (mode)
- {
- case PIN_MODE_OUTPUT:
-
- value = !!pl061_read8((BIT(pin + 2)));
-
- pl061_write8(BIT(pin + 2), 0 << pin);
- gpiodir = pl061_read8(GPIODIR);
- gpiodir |= BIT(pin);
- pl061_write8(GPIODIR, gpiodir);
-
- /*
- * gpio value is set again, because pl061 doesn't allow to set value of
- * a gpio pin before configuring it in OUT mode.
- */
- pl061_write8((BIT(pin + 2)), value << pin);
-
- break;
- case PIN_MODE_INPUT:
-
- gpiodir = pl061_read8(GPIODIR);
- gpiodir &= ~(BIT(pin));
- pl061_write8(GPIODIR, gpiodir);
-
- break;
- }
-
-#ifdef RT_USING_SMP
- rt_spin_unlock_irqrestore(&_pl061.spinlock, level);
-#endif
-}
-
-static void pl061_pin_write(struct rt_device *device, rt_base_t pin, rt_uint8_t value)
-{
- pl061_write8(BIT(pin + 2), !!value << pin);
-}
-
-static rt_ssize_t pl061_pin_read(struct rt_device *device, rt_base_t pin)
-{
- return !!pl061_read8((BIT(pin + 2)));
-}
-
-static rt_err_t pl061_pin_attach_irq(struct rt_device *device, rt_base_t pin, rt_uint8_t mode, void (*hdr)(void *args), void *args)
-{
- rt_uint8_t gpiois, gpioibe, gpioiev;
- rt_uint8_t bit = BIT(mode);
-#ifdef RT_USING_SMP
- rt_base_t level;
-#endif
-
- if (pin < 0 || pin >= PL061_GPIO_NR)
- {
- return -RT_EINVAL;
- }
-
-#ifdef RT_USING_SMP
- level = rt_spin_lock_irqsave(&_pl061.spinlock);
-#endif
-
- gpioiev = pl061_read8(GPIOIEV);
- gpiois = pl061_read8(GPIOIS);
- gpioibe = pl061_read8(GPIOIBE);
-
- if (mode == PIN_IRQ_MODE_HIGH_LEVEL || pin == PIN_IRQ_MODE_LOW_LEVEL)
- {
- rt_bool_t polarity = (mode == PIN_IRQ_MODE_HIGH_LEVEL);
-
- /* Disable edge detection */
- gpioibe &= ~bit;
- /* Enable level detection */
- gpiois |= bit;
-
- /* Select polarity */
- if (polarity)
- {
- gpioiev |= bit;
- }
- else
- {
- gpioiev &= ~bit;
- }
- }
- else if (mode == PIN_IRQ_MODE_RISING_FALLING)
- {
- /* Disable level detection */
- gpiois &= ~bit;
- /* Select both edges, setting this makes GPIOEV be ignored */
- gpioibe |= bit;
- }
- else if (mode == PIN_IRQ_MODE_RISING || mode == PIN_IRQ_MODE_FALLING)
- {
- rt_bool_t rising = (mode == PIN_IRQ_MODE_RISING);
-
- /* Disable level detection */
- gpiois &= ~bit;
- /* Clear detection on both edges */
- gpioibe &= ~bit;
-
- /* Select edge */
- if (rising)
- {
- gpioiev |= bit;
- }
- else
- {
- gpioiev &= ~bit;
- }
- }
- else
- {
- /* No trigger: disable everything */
- gpiois &= ~bit;
- gpioibe &= ~bit;
- gpioiev &= ~bit;
- }
-
- pl061_write8(GPIOIS, gpiois);
- pl061_write8(GPIOIBE, gpioibe);
- pl061_write8(GPIOIEV, gpioiev);
-
- _pl061.hdr[pin] = hdr;
- _pl061.args[pin] = args;
-
-#ifdef RT_USING_SMP
- rt_spin_unlock_irqrestore(&_pl061.spinlock, level);
-#endif
-
- return RT_EOK;
-}
-
-static rt_err_t pl061_pin_detach_irq(struct rt_device *device, rt_base_t pin)
-{
- if (pin < 0 || pin >= PL061_GPIO_NR)
- {
- return -RT_EINVAL;
- }
-
- _pl061.hdr[pin] = RT_NULL;
- _pl061.args[pin] = RT_NULL;
-
- return RT_EOK;
-}
-
-static rt_err_t pl061_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled)
-{
- rt_uint8_t mask = BIT(pin);
- rt_uint8_t gpioie;
-
-#ifdef RT_USING_SMP
- rt_base_t level;
-#endif
-
- if (pin < 0 || pin >= PL061_GPIO_NR)
- {
- return -RT_EINVAL;
- }
-
-#ifdef RT_USING_SMP
- level = rt_spin_lock_irqsave(&_pl061.spinlock);
-#endif
-
- if (enabled)
- {
- gpioie = pl061_read8(GPIOIE) | mask;
- }
- else
- {
- gpioie = pl061_read8(GPIOIE) & ~mask;
- }
-
- pl061_write8(GPIOIE, gpioie);
-
-#ifdef RT_USING_SMP
- rt_spin_unlock_irqrestore(&_pl061.spinlock, level);
-#endif
-
- return RT_EOK;
-}
-
-static const struct rt_pin_ops ops =
-{
- pl061_pin_mode,
- pl061_pin_write,
- pl061_pin_read,
- pl061_pin_attach_irq,
- pl061_pin_detach_irq,
- pl061_pin_irq_enable,
- RT_NULL,
-};
-
-static void rt_hw_gpio_isr(int irqno, void *param)
-{
- rt_uint8_t mask;
- unsigned long pending;
-
-#ifdef RT_USING_SMP
- rt_base_t level;
-#endif
-
- pending = pl061_read8(GPIOMIS);
-
- if (pending)
- {
- rt_base_t pin;
-
- for (pin = 0; pin < PL061_GPIO_NR; ++pin)
- {
- if (pending & BIT(pin))
- {
- mask |= BIT(pin);
-
- if (_pl061.hdr[pin] != RT_NULL)
- {
- _pl061.hdr[pin](_pl061.args[pin]);
- }
- }
- }
- }
-
-#ifdef RT_USING_SMP
- level = rt_spin_lock_irqsave(&_pl061.spinlock);
-#endif
-
- pl061_write8(GPIOIC, mask);
-
-#ifdef RT_USING_SMP
- rt_spin_unlock_irqrestore(&_pl061.spinlock, level);
-#endif
-}
-
-int rt_hw_gpio_init(void)
-{
-#ifdef RT_USING_SMP
- rt_spin_lock_init(&_pl061.spinlock);
-#endif
-
- pl061_gpio_base = (rt_size_t)rt_ioremap((void *)pl061_gpio_base, PL061_GPIO_SIZE);
-
- rt_device_pin_register("gpio", &ops, RT_NULL);
- rt_hw_interrupt_install(PL061_GPIO_IRQNUM, rt_hw_gpio_isr, RT_NULL, "gpio");
- rt_hw_interrupt_umask(PL061_GPIO_IRQNUM);
-
- return 0;
-}
-INIT_DEVICE_EXPORT(rt_hw_gpio_init);
-
-#endif /* BSP_USING_PIN */
diff --git a/bsp/qemu-virt64-aarch64/drivers/drv_gpio.h b/bsp/qemu-virt64-aarch64/drivers/drv_gpio.h
deleted file mode 100644
index 3f3e014f11..0000000000
--- a/bsp/qemu-virt64-aarch64/drivers/drv_gpio.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2022-6-30 GuEe-GUI first version
- */
-
-#ifndef DRV_GPIO_H__
-#define DRV_GPIO_H__
-
-int rt_hw_gpio_init(void);
-
-#endif
diff --git a/bsp/qemu-virt64-aarch64/drivers/drv_romfb.c b/bsp/qemu-virt64-aarch64/drivers/drv_romfb.c
new file mode 100755
index 0000000000..15ad89f443
--- /dev/null
+++ b/bsp/qemu-virt64-aarch64/drivers/drv_romfb.c
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#include <rtthread.h>
+
+void platform_get_ramfb_params(rt_uint32_t *width, rt_uint32_t *height)
+{
+ *width = RT_GRAPHIC_ROMFB_WIDTH;
+ *height = RT_GRAPHIC_ROMFB_HEIGHT;
+}
diff --git a/bsp/qemu-virt64-aarch64/drivers/drv_rtc.c b/bsp/qemu-virt64-aarch64/drivers/drv_rtc.c
deleted file mode 100644
index f0a041b70d..0000000000
--- a/bsp/qemu-virt64-aarch64/drivers/drv_rtc.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2021-11-4 GuEe-GUI first version
- */
-
-#include <rtthread.h>
-#include <rtdevice.h>
-#include <sys/time.h>
-#include <board.h>
-
-#include "drv_rtc.h"
-
-#ifdef BSP_USING_RTC
-
-#define RTC_DR 0x00 /* data read register */
-#define RTC_MR 0x04 /* match register */
-#define RTC_LR 0x08 /* data load register */
-#define RTC_CR 0x0c /* control register */
-#define RTC_IMSC 0x10 /* interrupt mask and set register */
-#define RTC_RIS 0x14 /* raw interrupt status register */
-#define RTC_MIS 0x18 /* masked interrupt status register */
-#define RTC_ICR 0x1c /* interrupt clear register */
-
-#define RTC_CR_OPEN 1
-#define RTC_CR_CLOSE 0
-
-static struct hw_rtc_device rtc_device;
-static rt_ubase_t pl031_rtc_base = PL031_RTC_BASE;
-
-rt_inline rt_uint32_t pl031_read32(rt_ubase_t offset)
-{
- return (*((volatile unsigned int *)(pl031_rtc_base + offset)));
-}
-
-rt_inline void pl031_write32(rt_ubase_t offset, rt_uint32_t value)
-{
- (*((volatile unsigned int *)(pl031_rtc_base + offset))) = value;
-}
-
-static rt_err_t pl031_rtc_init(rt_device_t dev)
-{
- return RT_EOK;
-}
-
-static rt_err_t pl031_rtc_open(rt_device_t dev, rt_uint16_t oflag)
-{
- pl031_write32(RTC_CR, RTC_CR_OPEN);
- return RT_EOK;
-}
-
-static rt_err_t pl031_rtc_close(rt_device_t dev)
-{
- pl031_write32(RTC_CR, RTC_CR_CLOSE);
- return RT_EOK;
-}
-
-static rt_err_t pl031_rtc_control(rt_device_t dev, int cmd, void *args)
-{
-
- RT_ASSERT(dev != RT_NULL);
-
- if (args == RT_NULL)
- {
- return -RT_EINVAL;
- }
-
- switch (cmd)
- {
- case RT_DEVICE_CTRL_RTC_GET_TIME:
- *(time_t *)args = pl031_read32(RTC_DR);
- break;
- case RT_DEVICE_CTRL_RTC_SET_TIME:
- pl031_write32(RTC_LR, *(time_t *)args);
- break;
- default:
- return -RT_EINVAL;
- }
- return RT_EOK;
-}
-
-static rt_ssize_t pl031_rtc_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
-{
- pl031_rtc_control(dev, RT_DEVICE_CTRL_RTC_GET_TIME, buffer);
- return size;
-}
-
-static rt_ssize_t pl031_rtc_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
-{
- pl031_rtc_control(dev, RT_DEVICE_CTRL_RTC_SET_TIME, (void *)buffer);
- return size;
-}
-
-const static struct rt_device_ops pl031_rtc_ops =
-{
- .init = pl031_rtc_init,
- .open = pl031_rtc_open,
- .close = pl031_rtc_close,
- .read = pl031_rtc_read,
- .write = pl031_rtc_write,
- .control = pl031_rtc_control
-};
-
-int rt_hw_rtc_init(void)
-{
- pl031_rtc_base = (rt_size_t)rt_ioremap((void *)pl031_rtc_base, PL031_RTC_SIZE);
-
- rt_memset(&rtc_device, 0, sizeof(rtc_device));
-
- rtc_device.device.type = RT_Device_Class_RTC;
- rtc_device.device.rx_indicate = RT_NULL;
- rtc_device.device.tx_complete = RT_NULL;
- rtc_device.device.ops = &pl031_rtc_ops;
- rtc_device.device.user_data = RT_NULL;
-
- /* register a rtc device */
- rt_device_register(&rtc_device.device, "rtc0", RT_DEVICE_FLAG_RDWR);
- rt_soft_rtc_set_source("rtc0");
- return 0;
-}
-INIT_DEVICE_EXPORT(rt_hw_rtc_init);
-#endif /* BSP_USING_RTC */
diff --git a/bsp/qemu-virt64-aarch64/drivers/drv_rtc.h b/bsp/qemu-virt64-aarch64/drivers/drv_rtc.h
deleted file mode 100644
index 15d4f01a9c..0000000000
--- a/bsp/qemu-virt64-aarch64/drivers/drv_rtc.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2021-11-4 GuEe-GUI first version
- */
-
-#ifndef DRV_RTC_H__
-#define DRV_RTC_H__
-
-#include <rtthread.h>
-#include <rtdevice.h>
-#include <time.h>
-
-struct hw_rtc_device
-{
- struct rt_device device;
-};
-
-int rt_hw_rtc_init(void);
-
-#endif
diff --git a/bsp/qemu-virt64-aarch64/drivers/drv_uart.c b/bsp/qemu-virt64-aarch64/drivers/drv_uart.c
deleted file mode 100644
index 12005d297d..0000000000
--- a/bsp/qemu-virt64-aarch64/drivers/drv_uart.c
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * serial.c UART driver
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2013-03-30 Bernard the first verion
- */
-
-#include <rthw.h>
-#include <rtdevice.h>
-
-#include "board.h"
-#include "mmu.h"
-
-struct hw_uart_device
-{
- rt_size_t hw_base;
- rt_size_t irqno;
-};
-
-#define UART_DR(base) __REG32(base + 0x00)
-#define UART_FR(base) __REG32(base + 0x18)
-#define UART_CR(base) __REG32(base + 0x30)
-#define UART_IMSC(base) __REG32(base + 0x38)
-#define UART_ICR(base) __REG32(base + 0x44)
-
-#define UARTFR_RXFE 0x10
-#define UARTFR_TXFF 0x20
-#define UARTIMSC_RXIM 0x10
-#define UARTIMSC_TXIM 0x20
-#define UARTICR_RXIC 0x10
-#define UARTICR_TXIC 0x20
-
-static void rt_hw_uart_isr(int irqno, void *param)
-{
- struct rt_serial_device *serial = (struct rt_serial_device *)param;
-
- rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
-}
-
-static rt_err_t uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
-{
- return RT_EOK;
-}
-
-static rt_err_t uart_control(struct rt_serial_device *serial, int cmd, void *arg)
-{
- struct hw_uart_device *uart;
-
- RT_ASSERT(serial != RT_NULL);
- uart = (struct hw_uart_device *)serial->parent.user_data;
-
- switch (cmd)
- {
- case RT_DEVICE_CTRL_CLR_INT:
- /* disable rx irq */
- UART_IMSC(uart->hw_base) &= ~UARTIMSC_RXIM;
- break;
-
- case RT_DEVICE_CTRL_SET_INT:
- /* enable rx irq */
- UART_IMSC(uart->hw_base) |= UARTIMSC_RXIM;
- rt_hw_interrupt_umask(uart->irqno);
- break;
-
- default:
- return -1;
- }
-
- return RT_EOK;
-}
-
-static int uart_putc(struct rt_serial_device *serial, char c)
-{
- struct hw_uart_device *uart;
-
- RT_ASSERT(serial != RT_NULL);
- uart = (struct hw_uart_device *)serial->parent.user_data;
-
- while (UART_FR(uart->hw_base) & UARTFR_TXFF);
- UART_DR(uart->hw_base) = c;
-
- return 1;
-}
-
-static int uart_getc(struct rt_serial_device *serial)
-{
- int ch;
- struct hw_uart_device *uart;
-
- RT_ASSERT(serial != RT_NULL);
- uart = (struct hw_uart_device *)serial->parent.user_data;
-
- ch = -1;
- if (!(UART_FR(uart->hw_base) & UARTFR_RXFE))
- {
- ch = UART_DR(uart->hw_base) & 0xff;
- }
-
- return ch;
-}
-
-static const struct rt_uart_ops _uart_ops =
-{
- uart_configure,
- uart_control,
- uart_putc,
- uart_getc,
-};
-
-#ifdef RT_USING_UART0
-/* UART device driver structure */
-static struct hw_uart_device _uart0_device =
-{
- PL011_UART0_BASE,
- PL011_UART0_IRQNUM,
-};
-static struct rt_serial_device _serial0;
-#endif
-
-int rt_hw_uart_init(void)
-{
- struct hw_uart_device *uart;
- struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
-
-#ifdef RT_USING_UART0
- _uart0_device.hw_base = (rt_size_t)rt_ioremap((void*)_uart0_device.hw_base, PL011_UART0_SIZE);
- uart = &_uart0_device;
-
- _serial0.ops = &_uart_ops;
- _serial0.config = config;
-
- /* register UART1 device */
- rt_hw_serial_register(&_serial0, "uart0",
- RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
- uart);
- rt_hw_interrupt_install(uart->irqno, rt_hw_uart_isr, &_serial0, "uart0");
- /* enable Rx and Tx of UART */
- UART_CR(uart->hw_base) = (1 << 0) | (1 << 8) | (1 << 9);
-#endif
-
- return 0;
-}
diff --git a/bsp/qemu-virt64-aarch64/drivers/drv_uart.h b/bsp/qemu-virt64-aarch64/drivers/drv_uart.h
deleted file mode 100644
index 1cd7a583ff..0000000000
--- a/bsp/qemu-virt64-aarch64/drivers/drv_uart.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2013-03-30 Bernard the first verion
- */
-
-#ifndef __DRV_UART_H__
-#define __DRV_UART_H__
-
-int rt_hw_uart_init(void);
-
-#endif /* __DRV_UART_H__ */
diff --git a/bsp/qemu-virt64-aarch64/drivers/drv_virtio.c b/bsp/qemu-virt64-aarch64/drivers/drv_virtio.c
deleted file mode 100644
index 2634da6773..0000000000
--- a/bsp/qemu-virt64-aarch64/drivers/drv_virtio.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2021-11-11 GuEe-GUI the first version
- */
-
-#include <rtthread.h>
-
-#include <virtio.h>
-#ifdef BSP_USING_VIRTIO_BLK
-#include <virtio_blk.h>
-#endif
-#ifdef BSP_USING_VIRTIO_NET
-#include <virtio_net.h>
-#endif
-#ifdef BSP_USING_VIRTIO_CONSOLE
-#include <virtio_console.h>
-#endif
-#ifdef BSP_USING_VIRTIO_GPU
-#include <virtio_gpu.h>
-#endif
-#ifdef BSP_USING_VIRTIO_INPUT
-#include <virtio_input.h>
-#endif
-
-#include <board.h>
-
-static virtio_device_init_handler virtio_device_init_handlers[] =
-{
-#ifdef BSP_USING_VIRTIO_BLK
- [VIRTIO_DEVICE_ID_BLOCK] = rt_virtio_blk_init,
-#endif
-#ifdef BSP_USING_VIRTIO_NET
- [VIRTIO_DEVICE_ID_NET] = rt_virtio_net_init,
-#endif
-#ifdef BSP_USING_VIRTIO_CONSOLE
- [VIRTIO_DEVICE_ID_CONSOLE] = rt_virtio_console_init,
-#endif
-#ifdef BSP_USING_VIRTIO_GPU
- [VIRTIO_DEVICE_ID_GPU] = rt_virtio_gpu_init,
-#endif
-#ifdef BSP_USING_VIRTIO_INPUT
- [VIRTIO_DEVICE_ID_INPUT] = rt_virtio_input_init,
-#endif
-};
-
-int rt_virtio_devices_init(void)
-{
- int i;
- rt_uint32_t irq = VIRTIO_IRQ_BASE;
- rt_ubase_t mmio_base = VIRTIO_MMIO_BASE;
- struct virtio_mmio_config *mmio_config;
- virtio_device_init_handler init_handler;
-
- if (sizeof(virtio_device_init_handlers) == 0)
- {
- /* The compiler will optimize the codes after here. */
- return 0;
- }
-
- mmio_base = (rt_ubase_t)rt_ioremap((void *)mmio_base, VIRTIO_MMIO_SIZE * VIRTIO_MAX_NR);
-
- if (mmio_base == RT_NULL)
- {
- return -RT_ERROR;
- }
-
- for (i = 0; i < VIRTIO_MAX_NR; ++i, ++irq, mmio_base += VIRTIO_MMIO_SIZE)
- {
- mmio_config = (struct virtio_mmio_config *)mmio_base;
-
- if (mmio_config->magic != VIRTIO_MAGIC_VALUE ||
- mmio_config->version != RT_USING_VIRTIO_VERSION ||
- mmio_config->vendor_id != VIRTIO_VENDOR_ID)
- {
- continue;
- }
-
- init_handler = virtio_device_init_handlers[mmio_config->device_id];
-
- if (init_handler != RT_NULL)
- {
- init_handler((rt_ubase_t *)mmio_base, irq);
- }
- }
-
- return 0;
-}
-INIT_DEVICE_EXPORT(rt_virtio_devices_init);
diff --git a/bsp/qemu-virt64-aarch64/drivers/drv_virtio.h b/bsp/qemu-virt64-aarch64/drivers/drv_virtio.h
deleted file mode 100644
index 954338a386..0000000000
--- a/bsp/qemu-virt64-aarch64/drivers/drv_virtio.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2021-11-11 GuEe-GUI the first version
- */
-
-#ifndef __DRV_VIRTIO_H__
-#define __DRV_VIRTIO_H__
-
-int rt_virtio_devices_init(void);
-
-#endif /* __DRV_VIRTIO_H__ */
diff --git a/bsp/qemu-virt64-aarch64/drivers/virt.h b/bsp/qemu-virt64-aarch64/drivers/virt.h
deleted file mode 100644
index 801e889bff..0000000000
--- a/bsp/qemu-virt64-aarch64/drivers/virt.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2021-02-17 GuEe-GUI the first version
- */
-
-#ifndef VIRT_H__
-#define VIRT_H__
-
-#include <rtdef.h>
-#include <ioremap.h>
-
-#ifdef RT_USING_SMART
-#include <mmu.h>
-#endif
-
-#define __REG32(x) (*((volatile unsigned int *)(x)))
-#define __REG16(x) (*((volatile unsigned short *)(x)))
-
-/* UART */
-#define PL011_UART0_BASE 0x09000000
-#define PL011_UART0_SIZE 0x00001000
-#define PL011_UART0_IRQNUM (32 + 1)
-
-/* RTC */
-#define PL031_RTC_BASE 0x9010000
-#define PL031_RTC_SIZE 0x00001000
-#define PL031_RTC_IRQNUM (32 + 2)
-
-/* GPIO */
-#define PL061_GPIO_BASE 0x09030000
-#define PL061_GPIO_SIZE 0x00001000
-#define PL061_GPIO_IRQNUM (32 + 7)
-
-/* VirtIO */
-#define VIRTIO_MMIO_BASE 0x0a000000
-#define VIRTIO_MMIO_SIZE 0x00000200
-#define VIRTIO_MAX_NR 32
-#define VIRTIO_IRQ_BASE (32 + 16)
-#define VIRTIO_VENDOR_ID 0x554d4551 /* "QEMU" */
-
-/* GIC */
-#define MAX_HANDLERS 96
-#define GIC_IRQ_START 0
-#define ARM_GIC_NR_IRQS 96
-#define ARM_GIC_MAX_NR 1
-
-#define IRQ_ARM_IPI_KICK 0
-#define IRQ_ARM_IPI_CALL 1
-
-/* GICv2 */
-#define GIC_PL390_DISTRIBUTOR_PPTR 0x08000000
-#define GIC_PL390_CONTROLLER_PPTR 0x08010000
-#define GIC_PL390_HYPERVISOR_BASE 0x08030000
-#define GIC_PL390_VIRTUAL_CPU_BASE 0x08040000
-
-/* GICv3 */
-#define GIC_PL500_DISTRIBUTOR_PPTR GIC_PL390_DISTRIBUTOR_PPTR
-#define GIC_PL500_REDISTRIBUTOR_PPTR 0x080a0000
-#define GIC_PL500_CONTROLLER_PPTR GIC_PL390_CONTROLLER_PPTR
-#define GIC_PL500_ITS_PPTR 0x08080000
-
-/* the basic constants and interfaces needed by gic */
-rt_inline rt_ubase_t platform_get_gic_dist_base(void)
-{
-#ifdef BSP_USING_GICV2
- return GIC_PL390_DISTRIBUTOR_PPTR;
-#else
- return GIC_PL500_DISTRIBUTOR_PPTR;
-#endif
-}
-
-rt_inline rt_ubase_t platform_get_gic_redist_base(void)
-{
- return GIC_PL500_REDISTRIBUTOR_PPTR;
-}
-
-rt_inline rt_ubase_t platform_get_gic_cpu_base(void)
-{
-#ifdef BSP_USING_GICV2
- return GIC_PL390_CONTROLLER_PPTR;
-#else
- return GIC_PL500_CONTROLLER_PPTR;
-#endif
-}
-
-rt_inline rt_ubase_t platform_get_gic_its_base(void)
-{
- return GIC_PL500_ITS_PPTR;
-}
-
-#endif
diff --git a/bsp/qemu-virt64-aarch64/qemu-debug.bat b/bsp/qemu-virt64-aarch64/qemu-debug.bat
deleted file mode 100644
index 8d47286bd2..0000000000
--- a/bsp/qemu-virt64-aarch64/qemu-debug.bat
+++ /dev/null
@@ -1,9 +0,0 @@
-@echo off
-if exist sd.bin goto run
-qemu-img create -f raw sd.bin 64M
-
-:run
-qemu-system-aarch64 -M virt,gic-version=2 -cpu cortex-a53 -smp 4 -kernel rtthread.bin -nographic ^
--drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 ^
--netdev user,id=net0 -device virtio-net-device,netdev=net0,bus=virtio-mmio-bus.1 -s -S ^
--device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0
diff --git a/bsp/qemu-virt64-aarch64/qemu-debug.sh b/bsp/qemu-virt64-aarch64/qemu-debug.sh
deleted file mode 100644
index 6e60986085..0000000000
--- a/bsp/qemu-virt64-aarch64/qemu-debug.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-if [ ! -f "sd.bin" ]; then
-dd if=/dev/zero of=sd.bin bs=1024 count=65536
-mkfs.fat sd.bin
-fi
-qemu-system-aarch64 -M virt,gic-version=2 -cpu cortex-a53 -m 128M -smp 4 -kernel rtthread.bin -nographic \
--drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \
--netdev user,id=net0 -device virtio-net-device,netdev=net0,bus=virtio-mmio-bus.1 -s -S \
--device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0
diff --git a/bsp/qemu-virt64-aarch64/qemu-device-camera.c b/bsp/qemu-virt64-aarch64/qemu-device-camera.c
new file mode 100755
index 0000000000..494fb7275e
--- /dev/null
+++ b/bsp/qemu-virt64-aarch64/qemu-device-camera.c
@@ -0,0 +1,709 @@
+#if 0
+rm -f /tmp/qemu-device-camera.sock
+gcc $0 -o qemu-device-camera -lvfio-user && LD_LIBRARY_PATH=/usr/local/lib/ ./qemu-device-camera $1
+rm -f /tmp/qemu-device-camera.sock qemu-device-camera
+exit
+#endif
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2026-04-07 GuEe-GUI the first version
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <linux/videodev2.h>
+#include <vfio-user/libvfio-user.h>
+
+#ifndef MFD_CLOEXEC
+#define MFD_CLOEXEC 0x0001U
+#endif
+
+#define DEBUG 0
+#define SOCKET_PATH "/tmp/qemu-device-camera.sock"
+
+#define BAR3_FRAME_COUNT_OFFSET 0x00
+#define BAR3_FRAME_READY_OFFSET 0x04
+#define BAR3_SIZE 0x1000
+
+struct vfio_camera
+{
+ void *bar0_width;
+ size_t bar0_width_size;
+ void *bar1_height;
+ size_t bar1_height_size;
+ void *bar2_framebuffer;
+ size_t bar2_framebuffer_size;
+ int bar2_fd; /* Shared memory fd for zero-copy */
+ void *bar3_status;
+ size_t bar3_status_size;
+ uint32_t frame_count;
+ uint32_t frame_ready;
+ int fd;
+ enum v4l2_buf_type type;
+ struct v4l2_format fmt;
+ struct video_buffer *buffers;
+ int buffer_count;
+ bool streaming;
+};
+
+struct video_buffer
+{
+ void *start;
+ size_t length;
+};
+
+static volatile sig_atomic_t running = 1;
+
+static void handle_signal(int sig)
+{
+ running = 0;
+}
+
+static void setup_signal(void)
+{
+ struct sigaction sa;
+ sa.sa_handler = handle_signal;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+}
+
+/* Create shared memory file descriptor */
+static int create_shmem_fd(size_t size)
+{
+ int fd;
+
+ /* Try memfd_create first (Linux 3.17+) */
+#ifdef __NR_memfd_create
+ fd = syscall(__NR_memfd_create, "camera_fb", MFD_CLOEXEC);
+ if (fd >= 0)
+ {
+ if (ftruncate(fd, size) == 0)
+ {
+ return fd;
+ }
+ close(fd);
+ }
+#endif
+
+ /* Fallback: use shm_open */
+ fd = shm_open("/camera_framebuffer", O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (fd >= 0)
+ {
+ /* Unlink but keep fd */
+ shm_unlink("/camera_framebuffer");
+ if (ftruncate(fd, size) == 0)
+ {
+ return fd;
+ }
+ close(fd);
+ }
+
+ return -1;
+}
+
+static inline uint64_t round_up_pow2_u64(uint64_t x)
+{
+ if (x <= 1)
+ {
+ return 1;
+ }
+ return 1ULL << (64 - __builtin_clzll(x - 1));
+}
+
+static inline unsigned char clamp(int v)
+{
+ if (v < 0)
+ {
+ return 0;
+ }
+ if (v > 255)
+ {
+ return 255;
+ }
+ return v;
+}
+
+static inline void yuyv_to_rgb_pair(unsigned char *src, unsigned char *dst)
+{
+ int y0 = src[0];
+ int u = src[1] - 128;
+ int y1 = src[2];
+ int v = src[3] - 128;
+ int r, g, b;
+
+ /* pixel 0 */
+ r = y0 + ((1436 * v) >> 10);
+ g = y0 - ((352 * u + 731 * v) >> 10);
+ b = y0 + ((1815 * u) >> 10);
+ dst[0] = clamp(r);
+ dst[1] = clamp(g);
+ dst[2] = clamp(b);
+
+ /* pixel 1 */
+ r = y1 + ((1436 * v) >> 10);
+ g = y1 - ((352 * u + 731 * v) >> 10);
+ b = y1 + ((1815 * u) >> 10);
+ dst[3] = clamp(r);
+ dst[4] = clamp(g);
+ dst[5] = clamp(b);
+}
+
+/* Try to capture one frame (non-blocking) */
+static void try_capture_frame(struct vfio_camera *vc)
+{
+ struct v4l2_buffer buf;
+ unsigned char *src, *dst;
+ int pixels;
+
+ if (!vc->streaming)
+ {
+ return;
+ }
+
+ memset(&buf, 0, sizeof(buf));
+ buf.type = vc->type;
+ buf.memory = V4L2_MEMORY_MMAP;
+
+ /* Non-blocking dequeue */
+ if (ioctl(vc->fd, VIDIOC_DQBUF, &buf) < 0)
+ {
+ if (errno == EAGAIN)
+ {
+ /* No frame ready */
+ return;
+ }
+ perror("DQBUF");
+ return;
+ }
+
+ /* Convert YUYV to RGB24 */
+ if (buf.bytesused > 0)
+ {
+ src = vc->buffers[buf.index].start;
+ dst = vc->bar2_framebuffer;
+ pixels = vc->fmt.fmt.pix.width * vc->fmt.fmt.pix.height;
+
+ for (int i = 0; i < pixels; i += 2)
+ {
+ yuyv_to_rgb_pair(src, dst);
+ src += 4;
+ dst += 6;
+ }
+
+ /* Update frame status */
+ vc->frame_count++;
+ vc->frame_ready = 1;
+ memcpy((char *)vc->bar3_status + BAR3_FRAME_COUNT_OFFSET, &vc->frame_count, sizeof(uint32_t));
+ memcpy((char *)vc->bar3_status + BAR3_FRAME_READY_OFFSET, &vc->frame_ready, sizeof(uint32_t));
+ }
+
+ /* Re-queue buffer */
+ if (ioctl(vc->fd, VIDIOC_QBUF, &buf) < 0)
+ {
+ perror("QBUF");
+ }
+}
+
+static void get_max_resolution(int fd, uint32_t pixfmt, int *max_w, int *max_h)
+{
+ struct v4l2_frmsizeenum fsize;
+
+ memset(&fsize, 0, sizeof(fsize));
+
+ for (int i = 0; ; ++i)
+ {
+ fsize.index = i;
+ fsize.pixel_format = pixfmt;
+
+ if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsize) < 0)
+ {
+ break;
+ }
+
+ if (fsize.type == V4L2_FRMSIZE_TYPE_DISCRETE)
+ {
+ if (fsize.discrete.width * fsize.discrete.height > (*max_w) * (*max_h))
+ {
+ *max_w = fsize.discrete.width;
+ *max_h = fsize.discrete.height;
+ }
+ }
+ else if (fsize.type == V4L2_FRMSIZE_TYPE_STEPWISE ||
+ fsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS)
+ {
+ *max_w = fsize.stepwise.max_width;
+ *max_h = fsize.stepwise.max_height;
+ break;
+ }
+ }
+}
+
+static void vfio_log_cb(vfu_ctx_t *vfu_ctx, int level, const char *msg)
+{
+ fprintf(stderr, "[VFIO] %s", msg);
+}
+
+static ssize_t bar0_width_access(vfu_ctx_t *vfu_ctx, char *buf, size_t count,
+ loff_t offset, bool is_write)
+{
+ struct vfio_camera *vc = vfu_get_private(vfu_ctx);
+
+#if DEBUG
+ fprintf(stderr, "BAR0 access: offset=0x%lx, count=%zu, is_write=%d\n",
+ (unsigned long)offset, count, is_write);
+#endif
+
+ if (offset + count > vc->bar0_width_size)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!is_write)
+ {
+ memcpy(buf, (char *)vc->bar0_width + offset, count);
+ }
+
+ return (ssize_t)count;
+}
+
+static ssize_t bar1_height_access(vfu_ctx_t *vfu_ctx, char *buf, size_t count,
+ loff_t offset, bool is_write)
+{
+ struct vfio_camera *vc = vfu_get_private(vfu_ctx);
+
+#if DEBUG
+ fprintf(stderr, "BAR1 access: offset=0x%lx, count=%zu, is_write=%d\n",
+ (unsigned long)offset, count, is_write);
+#endif
+
+ if (offset + count > vc->bar1_height_size)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!is_write)
+ {
+ memcpy(buf, (char *)vc->bar1_height + offset, count);
+ }
+
+ return (ssize_t)count;
+}
+
+static ssize_t bar2_framebuffer_access(vfu_ctx_t *vfu_ctx, char *buf, size_t count,
+ loff_t offset, bool is_write)
+{
+ struct vfio_camera *vc = vfu_get_private(vfu_ctx);
+
+#if DEBUG
+ fprintf(stderr, "BAR2 access: offset=0x%lx, count=%zu, is_write=%d\n",
+ (unsigned long)offset, count, is_write);
+#endif
+
+ /* Try to capture a frame when framebuffer is accessed */
+ try_capture_frame(vc);
+
+ if (offset + count > vc->bar2_framebuffer_size)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!is_write)
+ {
+ memcpy(buf, (char *)vc->bar2_framebuffer + offset, count);
+ }
+
+ return (ssize_t)count;
+}
+
+static ssize_t bar3_status_access(vfu_ctx_t *vfu_ctx, char *buf, size_t count,
+ loff_t offset, bool is_write)
+{
+ struct vfio_camera *vc = vfu_get_private(vfu_ctx);
+
+#if DEBUG
+ fprintf(stderr, "BAR3 access: offset=0x%lx, count=%zu, is_write=%d\n",
+ (unsigned long)offset, count, is_write);
+#endif
+
+ /* Try to capture frame when status is read */
+ if (!is_write)
+ {
+ try_capture_frame(vc);
+ }
+
+ if (offset + count > vc->bar3_status_size)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!is_write)
+ {
+ memcpy(buf, (char *)vc->bar3_status + offset, count);
+ }
+ else
+ {
+ /* Allow RT-Thread to clear frame_ready flag */
+ memcpy((char *)vc->bar3_status + offset, buf, count);
+ if (offset <= BAR3_FRAME_READY_OFFSET && offset + count >= BAR3_FRAME_READY_OFFSET + sizeof(uint32_t))
+ {
+ memcpy(&vc->frame_ready, (char *)vc->bar3_status + BAR3_FRAME_READY_OFFSET, sizeof(uint32_t));
+ }
+ }
+
+ return (ssize_t)count;
+}
+
+static int device_reset(vfu_ctx_t *vfu_ctx, vfu_reset_type_t type)
+{
+ vfu_log(vfu_ctx, LOG_DEBUG, "Device reset");
+ return 0;
+}
+
+static void cleanup_buffers(struct video_buffer *buffers, int count)
+{
+ if (!buffers) return;
+
+ for (int i = 0; i < count; ++i)
+ {
+ if (buffers[i].start && buffers[i].start != MAP_FAILED)
+ {
+ munmap(buffers[i].start, buffers[i].length);
+ }
+ }
+ free(buffers);
+}
+
+int main(int argc, char *argv[])
+{
+ int fd, ret;
+ vfu_ctx_t *vfu_ctx;
+ struct vfio_camera vc;
+ struct v4l2_buffer buf;
+ struct v4l2_requestbuffers req;
+ size_t expected_frame_size;
+
+ memset(&vc, 0, sizeof(vc));
+ vc.bar2_fd = -1;
+
+ /* Open camera device */
+ fd = open(argc > 1 ? argv[1] : "/dev/video0", O_RDWR | O_NONBLOCK);
+ if (fd < 0)
+ {
+ perror("open camera");
+ return -1;
+ }
+ vc.fd = fd;
+
+ /* Set format */
+ memset(&vc.fmt, 0, sizeof(vc.fmt));
+ vc.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ vc.fmt.fmt.pix.width = 640;
+ vc.fmt.fmt.pix.height = 480;
+ vc.fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ vc.fmt.fmt.pix.field = V4L2_FIELD_ANY;
+
+ get_max_resolution(fd, vc.fmt.fmt.pix.pixelformat,
+ &vc.fmt.fmt.pix.width, &vc.fmt.fmt.pix.height);
+
+ if (ioctl(fd, VIDIOC_S_FMT, &vc.fmt) < 0)
+ {
+ perror("VIDIOC_S_FMT");
+ goto _err_close;
+ }
+
+ /* Request buffers */
+ memset(&req, 0, sizeof(req));
+ req.count = 4;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_MMAP;
+
+ if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0)
+ {
+ perror("REQBUFS");
+ goto _err_close;
+ }
+
+ vc.buffers = calloc(req.count, sizeof(*vc.buffers));
+ if (!vc.buffers)
+ {
+ perror("calloc buffers");
+ goto _err_close;
+ }
+ vc.buffer_count = req.count;
+
+ /* Map buffers */
+ for (int i = 0; i < req.count; ++i)
+ {
+ memset(&buf, 0, sizeof(buf));
+ buf.type = req.type;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = i;
+
+ if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0)
+ {
+ perror("QUERYBUF");
+ goto _err_cleanup;
+ }
+
+ vc.buffers[i].length = buf.length;
+ vc.buffers[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, buf.m.offset);
+ if (vc.buffers[i].start == MAP_FAILED)
+ {
+ perror("mmap");
+ goto _err_cleanup;
+ }
+ }
+
+ /* Queue buffers */
+ for (int i = 0; i < req.count; ++i)
+ {
+ memset(&buf, 0, sizeof(buf));
+ buf.type = req.type;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = i;
+
+ if (ioctl(fd, VIDIOC_QBUF, &buf) < 0)
+ {
+ perror("QBUF");
+ goto _err_streamoff;
+ }
+ }
+
+ /* Start streaming */
+ vc.type = req.type;
+ if (ioctl(fd, VIDIOC_STREAMON, &vc.type) < 0)
+ {
+ perror("STREAMON");
+ goto _err_streamoff;
+ }
+ vc.streaming = true;
+
+ /* Setup BARs */
+ expected_frame_size = vc.fmt.fmt.pix.width * vc.fmt.fmt.pix.height * 3;
+
+ vc.bar0_width_size = 0x1000;
+ vc.bar0_width = mmap(NULL, vc.bar0_width_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (vc.bar0_width == MAP_FAILED)
+ {
+ perror("mmap bar0");
+ goto _err_streamoff;
+ }
+ memcpy(vc.bar0_width, &vc.fmt.fmt.pix.width, sizeof(uint32_t));
+
+ vc.bar1_height_size = 0x1000;
+ vc.bar1_height = mmap(NULL, vc.bar1_height_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (vc.bar1_height == MAP_FAILED)
+ {
+ perror("mmap bar1");
+ goto _err_unmap_bar0;
+ }
+ memcpy(vc.bar1_height, &vc.fmt.fmt.pix.height, sizeof(uint32_t));
+
+ /* Allocate framebuffer with shared memory for zero-copy */
+ vc.bar2_framebuffer_size = round_up_pow2_u64(expected_frame_size);
+ vc.bar2_fd = create_shmem_fd(vc.bar2_framebuffer_size);
+ if (vc.bar2_fd < 0)
+ {
+ perror("create_shmem_fd");
+ goto _err_unmap_bar1;
+ }
+
+ vc.bar2_framebuffer = mmap(NULL, vc.bar2_framebuffer_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED, vc.bar2_fd, 0);
+ if (vc.bar2_framebuffer == MAP_FAILED)
+ {
+ perror("mmap bar2");
+ close(vc.bar2_fd);
+ goto _err_unmap_bar1;
+ }
+ memset(vc.bar2_framebuffer, 0, vc.bar2_framebuffer_size);
+
+ vc.bar3_status_size = BAR3_SIZE;
+ vc.bar3_status = mmap(NULL, vc.bar3_status_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (vc.bar3_status == MAP_FAILED)
+ {
+ perror("mmap bar3");
+ goto _err_unmap_bar2;
+ }
+ vc.frame_count = 0;
+ vc.frame_ready = 0;
+ memcpy((char *)vc.bar3_status + BAR3_FRAME_COUNT_OFFSET, &vc.frame_count, sizeof(uint32_t));
+ memcpy((char *)vc.bar3_status + BAR3_FRAME_READY_OFFSET, &vc.frame_ready, sizeof(uint32_t));
+
+ /* Create VFIO context */
+ vfu_ctx = vfu_create_ctx(VFU_TRANS_SOCK, SOCKET_PATH, 0, &vc, VFU_DEV_TYPE_PCI);
+ if (!vfu_ctx)
+ {
+ perror("vfu_create_ctx");
+ goto _err_unmap_bar3;
+ }
+
+ ret = vfu_setup_log(vfu_ctx, vfio_log_cb, DEBUG ? LOG_DEBUG : LOG_ERR);
+ if (ret < 0)
+ {
+ perror("vfu_setup_log");
+ goto _err_destroy_ctx;
+ }
+
+ ret = vfu_pci_init(vfu_ctx, VFU_PCI_TYPE_CONVENTIONAL, PCI_HEADER_TYPE_NORMAL, 0);
+ if (ret < 0)
+ {
+ perror("vfu_pci_init");
+ goto _err_destroy_ctx;
+ }
+
+ vfu_pci_set_id(vfu_ctx, 0x1234, 0x5678, 0x0000, 0x0000);
+ vfu_pci_set_class(vfu_ctx, 0xff, 0x00, 0x00);
+
+ /* Setup BAR regions */
+ ret = vfu_setup_region(vfu_ctx, VFU_PCI_DEV_BAR0_REGION_IDX, vc.bar0_width_size,
+ bar0_width_access, VFU_REGION_FLAG_READ | VFU_REGION_FLAG_MEM,
+ NULL, 0, -1, 0);
+ if (ret < 0)
+ {
+ perror("vfu_setup_region BAR0");
+ goto _err_destroy_ctx;
+ }
+
+ ret = vfu_setup_region(vfu_ctx, VFU_PCI_DEV_BAR1_REGION_IDX, vc.bar1_height_size,
+ bar1_height_access, VFU_REGION_FLAG_READ | VFU_REGION_FLAG_MEM,
+ NULL, 0, -1, 0);
+ if (ret < 0)
+ {
+ perror("vfu_setup_region BAR1");
+ goto _err_destroy_ctx;
+ }
+
+ /* BAR2: Framebuffer region with zero-copy mmap */
+ struct iovec bar2_mmap_areas[1] =
+ {
+ { .iov_base = 0, .iov_len = expected_frame_size }
+ };
+
+ ret = vfu_setup_region(vfu_ctx, VFU_PCI_DEV_BAR2_REGION_IDX, vc.bar2_framebuffer_size,
+ bar2_framebuffer_access, /* Fallback callback for non-mmap access */
+ VFU_REGION_FLAG_READ | VFU_REGION_FLAG_MEM,
+ bar2_mmap_areas, 1, vc.bar2_fd, 0);
+ if (ret < 0)
+ {
+ perror("vfu_setup_region BAR2");
+ goto _err_destroy_ctx;
+ }
+
+ ret = vfu_setup_region(vfu_ctx, VFU_PCI_DEV_BAR3_REGION_IDX, vc.bar3_status_size,
+ bar3_status_access, VFU_REGION_FLAG_READ | VFU_REGION_FLAG_MEM,
+ NULL, 0, -1, 0);
+ if (ret < 0)
+ {
+ perror("vfu_setup_region BAR3");
+ goto _err_destroy_ctx;
+ }
+
+ ret = vfu_setup_device_reset_cb(vfu_ctx, device_reset);
+ if (ret < 0)
+ {
+ perror("vfu_setup_device_reset_cb");
+ goto _err_destroy_ctx;
+ }
+
+ ret = vfu_setup_device_nr_irqs(vfu_ctx, VFU_DEV_INTX_IRQ, 1);
+ if (ret < 0)
+ {
+ perror("vfu_setup_device_nr_irqs");
+ goto _err_destroy_ctx;
+ }
+
+ ret = vfu_realize_ctx(vfu_ctx);
+ if (ret < 0)
+ {
+ perror("vfu_realize_ctx");
+ goto _err_destroy_ctx;
+ }
+
+ ret = vfu_attach_ctx(vfu_ctx);
+ if (ret < 0)
+ {
+ perror("vfu_attach_ctx");
+ goto _err_destroy_ctx;
+ }
+
+ setup_signal();
+
+ /* Main loop */
+ while (running)
+ {
+ ret = vfu_run_ctx(vfu_ctx);
+ if (ret < 0)
+ {
+ if (errno == ENOTCONN)
+ {
+ #if DEBUG
+ fprintf(stderr, "QEMU disconnected\n");
+ #endif
+
+ ret = vfu_attach_ctx(vfu_ctx);
+ if (ret < 0)
+ {
+ #if DEBUG
+ fprintf(stderr, "Failed to re-attach: %s\n", strerror(errno));
+ #endif
+ break;
+ }
+ #if DEBUG
+ fprintf(stderr, "Re-attached to QEMU\n");
+ #endif
+ continue;
+ }
+ perror("vfu_run_ctx");
+ break;
+ }
+ }
+
+_err_destroy_ctx:
+ vfu_destroy_ctx(vfu_ctx);
+_err_unmap_bar3:
+ munmap(vc.bar3_status, vc.bar3_status_size);
+_err_unmap_bar2:
+ if (vc.bar2_fd >= 0)
+ {
+ close(vc.bar2_fd);
+ }
+ munmap(vc.bar2_framebuffer, vc.bar2_framebuffer_size);
+_err_unmap_bar1:
+ munmap(vc.bar1_height, vc.bar1_height_size);
+_err_unmap_bar0:
+ munmap(vc.bar0_width, vc.bar0_width_size);
+_err_streamoff:
+ ioctl(fd, VIDIOC_STREAMOFF, &vc.type);
+_err_cleanup:
+ cleanup_buffers(vc.buffers, vc.buffer_count);
+_err_close:
+ close(fd);
+
+ return 0;
+}
diff --git a/bsp/qemu-virt64-aarch64/qemu-graphic.bat b/bsp/qemu-virt64-aarch64/qemu-graphic.bat
deleted file mode 100644
index 74332bb5ee..0000000000
--- a/bsp/qemu-virt64-aarch64/qemu-graphic.bat
+++ /dev/null
@@ -1,13 +0,0 @@
-@echo off
-if exist sd.bin goto run
-qemu-img create -f raw sd.bin 64M
-
-:run
-qemu-system-aarch64 -M virt,gic-version=2 -cpu cortex-a53 -smp 4 -kernel rtthread.bin -serial stdio ^
--drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 ^
--netdev user,id=net0 -device virtio-net-device,netdev=net0,bus=virtio-mmio-bus.1 ^
--device virtio-gpu-device,xres=800,yres=600,bus=virtio-mmio-bus.2 ^
--device virtio-keyboard-device,bus=virtio-mmio-bus.3 ^
--device virtio-mouse-device,bus=virtio-mmio-bus.4 ^
--device virtio-tablet-device,bus=virtio-mmio-bus.5 ^
--device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0
diff --git a/bsp/qemu-virt64-aarch64/qemu-graphic.sh b/bsp/qemu-virt64-aarch64/qemu-graphic.sh
deleted file mode 100644
index 52e78ba177..0000000000
--- a/bsp/qemu-virt64-aarch64/qemu-graphic.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-if [ ! -f "sd.bin" ]; then
-dd if=/dev/zero of=sd.bin bs=1024 count=65536
-mkfs.fat sd.bin
-fi
-qemu-system-aarch64 -M virt,gic-version=2 -cpu cortex-a53 -smp 4 -kernel rtthread.bin -serial stdio \
--drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \
--netdev user,id=net0 -device virtio-net-device,netdev=net0,bus=virtio-mmio-bus.1 \
--device virtio-gpu-device,xres=800,yres=600,bus=virtio-mmio-bus.2 \
--device virtio-keyboard-device,bus=virtio-mmio-bus.3 \
--device virtio-mouse-device,bus=virtio-mmio-bus.4 \
--device virtio-tablet-device,bus=virtio-mmio-bus.5 \
--device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0
diff --git a/bsp/qemu-virt64-aarch64/qemu.bat b/bsp/qemu-virt64-aarch64/qemu.bat
deleted file mode 100644
index de95884493..0000000000
--- a/bsp/qemu-virt64-aarch64/qemu.bat
+++ /dev/null
@@ -1,9 +0,0 @@
-@echo off
-if exist sd.bin goto run
-qemu-img create -f raw sd.bin 64M
-
-:run
-qemu-system-aarch64 -M virt,gic-version=2 -cpu cortex-a53 -smp 4 -kernel rtthread.bin -nographic ^
--drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 ^
--netdev user,id=net0 -device virtio-net-device,netdev=net0,bus=virtio-mmio-bus.1 ^
--device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0
diff --git a/bsp/qemu-virt64-aarch64/qemu.py b/bsp/qemu-virt64-aarch64/qemu.py
new file mode 100755
index 0000000000..91b1ed1e33
--- /dev/null
+++ b/bsp/qemu-virt64-aarch64/qemu.py
@@ -0,0 +1,380 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+import os, sys, re, shutil
+
+with open('rtconfig.h', 'r') as file:
+ rtconfig_header = file.read()
+
+if os.getenv('RTT_QEMU_ROOT'):
+ RTT_QEMU_ROOT = os.getenv('RTT_QEMU_ROOT')
+else:
+ RTT_QEMU_ROOT = ""
+
+if os.getenv('RTT_ROOT'):
+ RTT_ROOT = os.getenv('RTT_ROOT')
+else:
+ RTT_ROOT = os.path.join(os.getcwd(), '..', '..')
+
+sys.path.append(RTT_ROOT + '/tools')
+
+from building import *
+import dtc
+
+# WSL?
+is_windows = sys.platform.startswith('win') or \
+ os.popen('which qemu-system-aarch64 | xargs file').read().find('PE') >= 0 or \
+ (os.system("readlink `which qemu-system-aarch64` > /dev/null") == 0 and \
+ os.popen('readlink `which qemu-system-aarch64` | xargs file').read().find('PE') >= 0)
+
+class QEMU_VERSION:
+ def __init__(self):
+ cmd = os.popen(RTT_QEMU_ROOT + "qemu-system-aarch64 --version").readlines()[0]
+ version = cmd[cmd.find("version ") + 8: -1].split('.')
+
+ self.major = version[0]
+ self.minor = version[1]
+ self.revision = version[2]
+ # ==
+ def __eq__(self, version_in):
+ version = version_in.split('.')
+ return self.major == version[0] and self.minor == version[1] and self.revision == version[2]
+ # >=
+ def __ge__(self, version_in):
+ return self.__gt__(version_in) or self.__eq__(version_in)
+ # >
+ def __gt__(self, version_in):
+ version = version_in.split('.')
+ return int(self.major) > int(version[0]) or \
+ (int(self.major) == int(version[0]) and int(self.minor) > int(version[1])) or \
+ (int(self.major) == int(version[0]) and int(self.minor) == int(version[1]) and int(self.revision) > int(version[2]))
+ # <=
+ def __le__(self, version_in):
+ return self.__lt__(version_in) or self.__eq__(version_in)
+ # <
+ def __lt__(self, version_in):
+ return not self.__ge__(version_in)
+ # !=
+ def __ne__(self, version_in):
+ return not self.__eq__(version_in)
+
+ def toString(self):
+ return self.major + '.' + self.minor + '.' + self.revision
+
+qemu_version = QEMU_VERSION()
+
+opt = sys.argv
+
+graphic_cfg = """ \
+ -device ramfb \
+ -device virtio-keyboard-device \
+ -device virtio-tablet-device \
+"""
+
+smmu_cfg = ""
+iommu_cfg = ""
+cdrom_cfg = ""
+virtfs_cfg = ""
+ufs_cfg = ""
+camera_cfg = ""
+amp_cfg = ""
+
+q_gic = 2
+q_dumpdtb = ""
+q_el = 1
+q_smp = int(re.findall(r'#define\s+RT_CPUS_NR\s+(\d+)', rtconfig_header)[0])
+q_mem = 128
+q_graphic = "-nographic"
+q_debug = ""
+q_bootargs = "console=ttyAMA0 earlycon cma=8M coherent_pool=2M root=vda0 rootfstype=elm rootwait rw"
+q_initrd = ""
+q_block = "virtio"
+q_net = "user"
+q_ssh = 12055
+q_scsi = "scsi"
+q_cdrom = ""
+q_flash = "flash"
+q_emmc = "emmc"
+q_nvme = "nvme"
+q_ahci = "ahci"
+q_gl = None
+q_9p = ""
+q_ufs = "ufs"
+q_camera = ""
+q_dtbo = []
+q_dtb = ""
+
+def is_opt(key, inkey):
+ if str("-" + key) == inkey:
+ return True
+ return False
+
+for i in range(len(opt)):
+ if i == 0:
+ continue
+ inkey = opt[i]
+
+ if is_opt("gic", inkey): q_gic = int(opt[i + 1])
+ if is_opt("dumpdtb", inkey): q_dumpdtb = str(",dumpdtb=" + opt[i + 1])
+ if is_opt("el", inkey): q_el = int(opt[i + 1])
+ if is_opt("smp", inkey): q_smp = int(opt[i + 1])
+ if is_opt("mem", inkey): q_mem = int(opt[i + 1])
+ if is_opt("debug", inkey): q_debug = "-S -s"
+ if is_opt("bootargs", inkey): q_bootargs = opt[i + 1]
+ if is_opt("initrd", inkey): q_initrd = str("-initrd " + opt[i + 1])
+ if is_opt("graphic", inkey): q_graphic = graphic_cfg
+ if is_opt("virtio-block", inkey): q_block = opt[i + 1]
+ if is_opt("tap", inkey): q_net = "tap,ifname=tap0"
+ if is_opt("ssh", inkey): q_ssh = int(opt[i + 1])
+ if is_opt("cdrom", inkey): q_cdrom = opt[i + 1]
+ if is_opt("flash", inkey): q_flash = opt[i + 1]
+ if is_opt("emmc", inkey): q_emmc = opt[i + 1]
+ if is_opt("nvme", inkey): q_nvme = opt[i + 1]
+ if is_opt("gl", inkey): q_gl = "-device virtio-gpu-gl-pci -display {},gl=on ".format(opt[i + 1])
+ if is_opt("9p", inkey): q_9p = opt[i + 1]
+ if is_opt("ufs", inkey): q_ufs = opt[i + 1]
+ if is_opt("camera", inkey): q_camera = opt[i + 1]
+ if is_opt("dtb", inkey): q_dtb = "-dtb " + opt[i + 1]
+ if is_opt("dtbo", inkey): q_dtbo += [opt[i + 1]]
+
+# SMP
+if q_smp > 8:
+ q_gic = 3
+
+# Exception Level
+if q_el == 1:
+ q_el = ""
+elif q_el == 2:
+ q_el = ",virtualization=on"
+ if q_gic == 3:
+ q_gic = "max"
+elif q_el == 3:
+ q_el = ",secure=on"
+else:
+ print("Error: Invalid -el {}".format(q_el))
+ exit(-1)
+
+# Display
+# --enable-opengl --enable-virglrenderer
+if q_graphic != "-nographic":
+ if q_gl:
+ q_graphic += q_gl
+ else:
+ q_graphic += "-device virtio-gpu-device "
+elif q_gl != None:
+ print("Error: GL should in graphic mode")
+ exit(-1)
+
+# Net
+# --enable-slirp
+# Enable TAP in example (not support in Windows, Maybe WSL2):
+# ETH0=`/sbin/route -n | awk '$1 == "0.0.0.0" {print $NF}'`
+# ip link add br0 type bridge
+# ip link set br0 up
+# echo 1 | tee /proc/sys/net/ipv4/ip_forward > /dev/null
+# ip link set ${ETH0} up
+# ip link set ${ETH0} master br0
+# dhclient br0
+# ip tuntap add dev tap0 mode tap
+# ip link set dev tap0 up
+# ip link set tap0 master br0
+#
+# Disable TAP in example (not support in Windows, Maybe WSL2):
+# ip link set tap0 down
+# ip tuntap del dev tap0 mod tap
+# echo 0 | tee /proc/sys/net/ipv4/ip_forward > /dev/null
+# ip link set br0 down
+# ip link del br0 type bridge
+if q_net.find("user") >= 0:
+ q_net += ",hostfwd=tcp::{}-:22".format(q_ssh)
+else:
+ if not is_windows:
+ q_net += ",script=no,downscript=no"
+ print("Warning: SSH not set in TAP")
+
+# Storage
+# pflash have pflash0 and pflash1, pflash0 is used for BootROMs such as UEFI
+# if we load file to pflash0, QEMU will boot from it, so we only use pflash1.
+# Well, we can R/W in pflash0 by CFI driver, but the data will lost after QEMU exits.
+#
+# partitions (not support in Windows, Maybe WSL2):
+# modprobe nbd max_part=12
+# qemu-nbd --connect=/dev/nbdX ABC.qcow2
+# fdisk /dev/nbdX
+# ...
+# qemu-nbd --disconnect /dev/nbdX
+disk_list = [q_block, q_scsi, q_flash, q_emmc, q_nvme, q_ahci]
+
+if qemu_version >= '8.2.0':
+ disk_list += [q_ufs]
+ ufs_cfg = """ \
+ -drive if=none,file={}.qcow2,format=qcow2,id=ufs \
+ -device ufs,serial=deadbeef \
+ -device ufs-lu,drive=ufs \
+ """.format(q_ufs)
+
+for disk in disk_list:
+ disk += ".qcow2"
+ if not os.path.exists(disk):
+ os.system("qemu-img create -f qcow2 {} 64M".format(disk))
+
+if len(q_cdrom) > 0:
+ cdrom_cfg = """ \
+ -device scsi-cd,channel=0,scsi-id=0,lun=1,drive=scsi1 \
+ -drive file={},format=raw,if=none,id=scsi1 \
+ """.format(q_cdrom)
+
+# Share File System
+# --enable-virtfs
+if len(q_9p) > 0:
+ p9_tag = "hostshare"
+ virtfs_cfg = """ \
+ -fsdev local,security_model=passthrough,id=fsdev0,path={} \
+ -device virtio-9p-device,fsdev=fsdev0,mount_tag={} \
+ """.format(q_9p, p9_tag)
+ q_bootargs += " 9p.tag={} 9p.mount=host".format(p9_tag)
+
+# Camera (Linux only)
+if len(q_camera) > 0:
+ if is_windows:
+ print("Camera is supported only on Linux")
+ exit(-1)
+ camera_cfg = """ --device '{"driver": "vfio-user-pci","socket": {"path": "/tmp/qemu-device-camera.sock", "type": "unix"}}'"""
+
+# Note
+# Hot-plug / Hot-unplug in QEMU monitor:
+# (qemu) chardev-add socket,host=127.0.0.1,port=4323,server=on,wait=off,telnet=on,id=console2
+# (qemu) device_add virtserialport,chardev=console2,name=org.rt-thread.port,id=port2
+# (qemu) device_del port2
+# (qemu) chardev-remove console2
+#
+# VirtIO version disable legacy to set version >= 1.0:
+# -global virtio-mmio.force-legacy=false
+#
+# VirtIO used virtqueue packed (version >= 1.0)
+# -device virtio-XYZ-device,packed=on
+#
+# VirtIO used PCI/PCIe bus (version >= 1.0)
+# -device virtio-XYZ-pci,disable-legacy=on
+
+cmd_base = """
+{}qemu-system-aarch64 \
+ -M virt,acpi=on,its=on,gic-version={}{}{} \
+ -cpu max \
+ -smp {} \
+ -m {} \
+ -kernel rtthread.bin \
+ -append "{}" \
+ {} \
+ {} \
+ -serial mon:stdio \
+ {} \
+ {} \
+ -drive if=none,file={}.qcow2,format=qcow2,id=blk0 \
+ -device virtio-blk-device,drive=blk0 \
+ -netdev {},id=net0 \
+ -device virtio-net-device,netdev=net0,speed=800000 \
+ -device virtio-rng-device \
+ -device virtio-scsi-pci,disable-legacy=on \
+ -device scsi-hd,channel=0,scsi-id=0,lun=0,drive=scsi0 \
+ -drive file={}.qcow2,format=qcow2,if=none,id=scsi0 \
+ {} \
+ {} \
+ -device virtio-crypto-device,cryptodev=vcrypto0 \
+ -object cryptodev-backend-builtin,id=vcrypto0 \
+ -device virtio-serial-device \
+ -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 \
+ -device virtserialport,chardev=console0,name=org.rt-thread.console \
+ -drive if=pflash,file={}.qcow2,format=qcow2,index=1 \
+ -device pci-serial,chardev=console1 \
+ -chardev socket,host=127.0.0.1,port=4322,server=on,wait=off,telnet=on,id=console1 \
+ -device sdhci-pci -device sd-card,drive=emmc0 \
+ -drive if=none,file={}.qcow2,format=qcow2,id=emmc0 \
+ -device nvme,id=nvme-ctrl-0,serial=deadbeef \
+ -drive if=none,file={}.qcow2,format=qcow2,id=nvme0 \
+ -device nvme-ns,drive=nvme0 \
+ -device ahci,id=ahci-ctrl-0 \
+ -drive if=none,file={}.qcow2,format=qcow2,id=ahci0 \
+ -device ide-hd,drive=ahci0 \
+ -device i6300esb -watchdog-action reset \
+ {} \
+ {} \
+ {} \
+ -device edu,dma_mask=0xffffffff
+"""
+def cmd():
+ return cmd_base.format(RTT_QEMU_ROOT, q_gic, q_dumpdtb, q_el, q_smp, q_mem, q_bootargs, q_dtb, q_initrd,
+ q_graphic, q_debug, q_block, q_net, q_scsi, cdrom_cfg, virtfs_cfg,
+ q_flash, q_emmc, q_nvme, q_ahci, ufs_cfg, camera_cfg, amp_cfg)
+
+def dumpdtb():
+ dtb = q_dumpdtb.split('=')[-1]
+ dtc.dtb_to_dts(RTT_ROOT, dtb)
+
+if len(q_camera) > 0:
+ shell = os.environ.get('SHELL', '/bin/bash')
+ os.system("{} ./qemu-device-camera.c {} &".format(shell, q_camera))
+
+ import time
+ start_time = time.time()
+ while not os.path.exists("/tmp/qemu-device-camera.sock"):
+ if time.time() - start_time > 5:
+ print("qemu-device-camera timeout")
+ exit(-1)
+ time.sleep(1)
+
+if len(q_dtbo) > 0:
+ amp_mode = False
+ q_bootargs = "rpmsg.mode=master " + q_bootargs
+ # Read all dtbo
+ with open("qemu.run.dtsi", 'w') as run_dtsi:
+ for dtsi in q_dtbo:
+ with open(dtsi) as dtsi:
+ dts_content = dtsi.read()
+ if not amp_mode and len(re.findall('amp_soc', dts_content)) > 0:
+ amp_cfg += " -device loader,file=amp.dtb,addr={}".format(re.findall(r'dtb-entry\s*=\s*<(\S+)>;', dts_content)[0])
+ amp_cfg += " -device loader,file=rtthread.bin,addr={}".format(re.findall(r'kernel-entry\s*=\s*<(\S+)>;', dts_content)[0])
+ q_smp += 1
+ amp_mode = True
+ run_dtsi.write(dts_content)
+ # Add Console for AMP
+ amp_cfg += " -serial telnet:localhost:4323,server,nowait"
+ # Add Storage for AMP
+ q_amp_block = q_block + ".amp.qcow2"
+ if not os.path.exists(q_amp_block):
+ shutil.copyfile(q_block + ".qcow2", q_amp_block)
+ amp_cfg += " -drive if=none,file={},format=qcow2,id=blk1 -device virtio-blk-device,drive=blk1".format(q_amp_block)
+ # Keep vda0 is root
+ cmd_base = cmd_base.replace(",drive=blk0", ",drive=blk0,bus=virtio-mmio-bus.0")
+ # Build AMP dtb
+ with open("amp.dt.h", 'w') as gic_inc:
+ gic_inc.write("#define GIC_VERSION {}\n\r".format(q_gic))
+ gic_inc.write("#define CPU_ID {}\n\r".format(q_smp - 1))
+ dtc.dts_to_dtb(RTT_ROOT, ["amp.dts"], ignore_warning = ["simple_bus_reg", "unit_address_vs_reg", "clocks_is_cell", "gpios_property"])
+ os.remove("amp.dt.h")
+ # Dump raw QEMU dts
+ q_dumpdtb = ",dumpdtb=qemu.run.dtb"
+ os.system(cmd())
+ dumpdtb()
+ os.remove("qemu.run.dtb")
+ q_dumpdtb = ""
+ # Merge all dtsi
+ with open("qemu.run.dts", 'a') as dts:
+ with open("qemu.run.dtsi", 'r') as dtsi:
+ dts.write(dtsi.read())
+ os.remove("qemu.run.dtsi")
+ # Build runtime dtb
+ dtc.dts_to_dtb(RTT_ROOT, ["qemu.run.dts"], ignore_warning = ["simple_bus_reg", "unit_address_vs_reg", "clocks_is_cell", "gpios_property"])
+ os.remove("qemu.run.dts")
+ # Run QEMU
+ q_dtb = "-dtb qemu.run.dtb"
+ os.system(cmd())
+ os.remove("amp.dtb")
+ os.remove("qemu.run.dtb")
+else:
+ os.system(cmd())
+
+ if len(q_dumpdtb) != 0:
+ dumpdtb()
+
+if len(q_camera) > 0:
+ os.system("kill -15 `pidof qemu-device-camera`")
diff --git a/bsp/qemu-virt64-aarch64/qemu.sh b/bsp/qemu-virt64-aarch64/qemu.sh
deleted file mode 100755
index 4f07acaad4..0000000000
--- a/bsp/qemu-virt64-aarch64/qemu.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-if [ ! -f "sd.bin" ]; then
-dd if=/dev/zero of=sd.bin bs=1024 count=65536
-mkfs.fat sd.bin
-fi
-qemu-system-aarch64 -M virt,gic-version=2 -cpu cortex-a53 -m 128M -smp 4 -kernel rtthread.bin -nographic \
--drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \
--netdev user,id=net0 -device virtio-net-device,netdev=net0,bus=virtio-mmio-bus.1 \
--device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0
diff --git a/bsp/qemu-virt64-aarch64/rtconfig.h b/bsp/qemu-virt64-aarch64/rtconfig.h
index ef33e3ed82..9de8955219 100644
--- a/bsp/qemu-virt64-aarch64/rtconfig.h
+++ b/bsp/qemu-virt64-aarch64/rtconfig.h
@@ -7,17 +7,6 @@
/* rt_vsnprintf options */
-#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
-#define RT_KLIBC_USING_VSNPRINTF_STANDARD
-#define RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS
-#define RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
-#define RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER
-#define RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER
-#define RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE 32
-#define RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE 32
-#define RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION 6
-#define RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 9
-#define RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS 4
/* end of rt_vsnprintf options */
/* rt_vsscanf options */
@@ -198,6 +187,7 @@
#define RT_DFS_ELM_MUTEX_TIMEOUT 3000
/* end of elm-chan's FatFs, Generic FAT Filesystem Module */
#define RT_USING_DFS_DEVFS
+#define RT_USING_DFS_9PFS
#define RT_USING_DFS_ROMFS
/* end of DFS: device virtual file system */
@@ -214,30 +204,106 @@
#define RT_USING_SERIAL_V1
#define RT_SERIAL_USING_DMA
#define RT_SERIAL_RB_BUFSZ 256
+#define RT_SERIAL_PL011
+#define RT_SERIAL_8250
+#define RT_SERIAL_8250_PCI
#define RT_USING_CLOCK_TIME
+#define RT_CLOCK_TIME_ARM_ARCH
#define RT_USING_NULL
#define RT_USING_ZERO
#define RT_USING_RANDOM
-#define RT_USING_PM
-#define PM_TICKLESS_THRESHOLD_TIME 2
+#define RT_USING_MTD_NOR
+#define RT_USING_MTD_NOR_CFI
#define RT_USING_RTC
-#define RT_USING_SOFT_RTC
+#define RT_USING_ALARM
+#define RT_ALARM_STACK_SIZE 8192
+#define RT_ALARM_TIMESLICE 5
+#define RT_ALARM_PRIORITY 10
+#define RT_RTC_PL031
+#define RT_USING_WDT
+#define RT_WDT_I6300ESB
+#define RT_USING_LCD
+#define RT_USING_GRAPHIC
+#define RT_GRAPHIC_FB
+#define RT_GRAPHIC_LOGO
+#define RT_GRAPHIC_LOGO_RT_THREAD_CLUT224
+#define RT_USING_HWCRYPTO
+#define RT_HWCRYPTO_DEFAULT_NAME "hwcryto"
+#define RT_HWCRYPTO_IV_MAX_SIZE 16
+#define RT_HWCRYPTO_KEYBIT_MAX_SIZE 256
+#define RT_HWCRYPTO_USING_AES
+#define RT_HWCRYPTO_USING_AES_ECB
+#define RT_HWCRYPTO_USING_AES_CBC
+#define RT_HWCRYPTO_USING_AES_CTR
+#define RT_HWCRYPTO_USING_DES
+#define RT_HWCRYPTO_USING_DES_ECB
+#define RT_HWCRYPTO_USING_DES_CBC
+#define RT_HWCRYPTO_USING_3DES
+#define RT_HWCRYPTO_USING_3DES_ECB
+#define RT_HWCRYPTO_USING_3DES_CBC
+#define RT_HWCRYPTO_USING_RC4
+#define RT_HWCRYPTO_USING_RNG
+#define RT_USING_INPUT
+#define RT_INPUT_POWER
+#define RT_INPUT_KEYBOARD
+#define RT_INPUT_KEYBOARD_GPIO
+#define RT_USING_ATA
+#define RT_ATA_AHCI
+#define RT_ATA_AHCI_PCI
+#define RT_USING_BLK
+
+/* Partition Types */
+
+#define RT_BLK_PARTITION_DFS
+#define RT_BLK_PARTITION_EFI
+/* end of Partition Types */
+#define RT_USING_SCSI
+#define RT_SCSI_SD
+#define RT_SCSI_CDROM
+#define RT_USING_FIRMWARE
+#define RT_FIRMWARE_QEMU_FW_CFG
+#define RT_USING_REGULATOR
/* Power Management (PM) Domains device drivers */
/* end of Power Management (PM) Domains device drivers */
#define RT_USING_VIRTIO
-#define RT_USING_VIRTIO10
-#define RT_USING_VIRTIO_MMIO_ALIGN
-#define RT_USING_VIRTIO_BLK
-#define RT_USING_VIRTIO_CONSOLE
-#define RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR 4
-#define RT_USING_VIRTIO_GPU
-#define RT_USING_VIRTIO_INPUT
+#define RT_VIRTIO_TRANSPORT_MMIO
+#define RT_VIRTIO_TRANSPORT_PCI
+#define RT_VIRTIO_BLK
+#define RT_VIRTIO_CONSOLE
+#define RT_VIRTIO_RNG
+#define RT_VIRTIO_SCSI
+#define RT_VIRTIO_RPROC_SERIAL
+#define RT_VIRTIO_GPU
+#define RT_VIRTIO_CRYPTO
+#define RT_USING_DMA
+#define RT_USING_MFD
+#define RT_MFD_EDU
+#define RT_MFD_SYSCON
#define RT_USING_OFW
#define RT_FDT_EARLYCON_MSG_SIZE 128
#define RT_USING_OFW_BUS_RANGES_NUMBER 8
+#define RT_USING_PCI
+#define RT_PCI_MSI
+#define RT_PCI_SYS_64BIT
+#define RT_PCI_CACHE_LINE_SIZE 8
+
+/* PCI Device Drivers */
+
+#define RT_PCI_ECAM
+#define RT_PCI_HOST_COMMON
+#define RT_PCI_HOST_GENERIC
+#define RT_USING_PIC
+#define MAX_HANDLERS 512
+#define RT_PIC_ARM_GIC
+#define RT_PIC_ARM_GIC_V2M
+#define RT_PIC_ARM_GIC_V3
+#define RT_PIC_ARM_GIC_V3_ITS
+#define RT_PIC_ARM_GIC_V3_ITS_IRQ_MAX 127
+#define RT_PIC_ARM_GIC_MAX_NR 1
#define RT_USING_PIN
+#define RT_PIN_PL061
#define RT_USING_CLK
/* SoC (System on Chip) Drivers */
@@ -528,19 +594,4 @@
/* end of RT-Thread online packages */
#define SOC_VIRT64_AARCH64
-/* AARCH64 qemu virt64 configs */
-
-#define BSP_SUPPORT_FPU
-#define BSP_USING_UART
-#define RT_USING_UART0
-#define BSP_USING_RTC
-#define BSP_USING_PIN
-#define BSP_USING_VIRTIO_BLK
-#define BSP_USING_VIRTIO_CONSOLE
-#define BSP_USING_VIRTIO_GPU
-#define BSP_USING_VIRTIO_INPUT
-#define BSP_USING_GIC
-#define BSP_USING_GICV2
-/* end of AARCH64 qemu virt64 configs */
-
#endif
diff --git a/components/drivers/rpmsg/Kconfig b/components/drivers/rpmsg/Kconfig
index 4d886af13c..2833208880 100755
--- a/components/drivers/rpmsg/Kconfig
+++ b/components/drivers/rpmsg/Kconfig
@@ -14,6 +14,12 @@ config RT_RPMSG_CHAR_MSG_SIZE_MAX
depends on RT_USING_RPMSG
default 256
+config RT_RPMSG_RT_THREAD_VIRTIO
+ bool "RT-Thread common VirtIO RPMSG"
+ depends on RT_USING_RPMSG
+ select RT_USING_SLAB
+ default y
+
if RT_USING_RPMSG
osource "$(SOC_DM_RPMSG_DIR)/Kconfig"
endif
diff --git a/components/drivers/rpmsg/SConscript b/components/drivers/rpmsg/SConscript
index a328d68ea3..370f2d8892 100755
--- a/components/drivers/rpmsg/SConscript
+++ b/components/drivers/rpmsg/SConscript
@@ -10,6 +10,9 @@ CPPPATH = [cwd + '/../include']
src = ['rpmsg.c', 'rpmsg_char.c', 'rpmsg_ns.c']
+if GetDepend(['RT_RPMSG_RT_THREAD_VIRTIO']):
+ src += ['rpmsg-rt-thread-virtio.c']
+
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
diff --git a/components/drivers/rpmsg/rpmsg-rt-thread-virtio.c b/components/drivers/rpmsg/rpmsg-rt-thread-virtio.c
new file mode 100644
index 0000000000..cae8234f66
--- /dev/null
+++ b/components/drivers/rpmsg/rpmsg-rt-thread-virtio.c
@@ -0,0 +1,565 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-25 GuEe-GUI the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include <mmu.h>
+
+#define DBG_TAG "rpmsg.rt-thread.virtio"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "../virtio/virtio_config/virtio-rpmsg.h"
+#include "../virtio/virtio_internal.h"
+
+/*
+ * RPMsg virtio AMP device driver
+ *
+ * The AMP OS device(s) may be instantiated in one of three equivalent way:
+ *
+ * Device Tree node, eg.:
+ *
+ * rpmsg_virtio_master {
+ * compatible = "rt-thread,virtio-rpmsg";
+ * shmem = <&soc_sram>;
+ * mboxes = <&soc_mailbox 0>;
+ * queue-max = <64>;
+ * master;
+ * }
+ *
+ * rpmsg_virtio_slave {
+ * compatible = "rt-thread,virtio-rpmsg";
+ * shmem = <&soc_sram>;
+ * mboxes = <&soc_mailbox 1>;
+ * queue-max = <64>;
+ * slave;
+ * }
+ *
+ * The property "queue-max" must be equal.
+ */
+
+#define RPMSG_VIRTIO_REG_MASTER_QUEUE_DESC 0x00 /* <RW> Virtual queue's master TX descriptor Area offset from share memory */
+#define RPMSG_VIRTIO_REG_MASTER_QUEUE_AVAIL 0x04 /* <RW> Virtual queue's master TX available Ring (Driver Area) offset from share memory */
+#define RPMSG_VIRTIO_REG_MASTER_QUEUE_USED 0x08 /* <RW> Virtual queue's master TX used Ring (Device Area) offset from share memory */
+#define RPMSG_VIRTIO_REG_SLAVE_QUEUE_DESC 0x0c /* <RW> ... slave TX ... */
+#define RPMSG_VIRTIO_REG_SLAVE_QUEUE_AVAIL 0x10 /* <RW> ... slave TX ... */
+#define RPMSG_VIRTIO_REG_SLAVE_QUEUE_USED 0x14 /* <RW> ... slave TX ... */
+
+#define RPMSG_VIRTIO_EVENT_LINK RT_BIT(0)
+#define RPMSG_VIRTIO_EVENT_QUEUE RT_BIT(1)
+
+typedef rt_uint32_t rpmsg_dma_tag;
+
+struct rpmsg_virtio
+{
+ struct rt_virtio_device parent;
+ struct rt_mbox_client mbox_client;
+
+ rt_bool_t is_master;
+ rt_uint8_t status;
+ rt_uint32_t queue_max;
+
+ void *shmem;
+ void *regs;
+ struct rt_mbox_chan *chan;
+
+ void *pool;
+ rt_slab_t slab;
+
+ rt_uint16_t next_idx;
+ struct virtq rx_virtq, peer_tx_virtq;
+
+ struct rt_spinlock spinlock;
+};
+
+#define raw_to_rpmsg_virtio(raw) rt_container_of(raw, struct rpmsg_virtio, parent)
+
+#define rpmsg_virtio_readl(rv, name) \
+ rt_le32_to_cpu(HWREG32((rv)->regs + RPMSG_VIRTIO_REG_##name))
+#define rpmsg_virtio_writel(rv, name, value) \
+ HWREG32((rv)->regs + RPMSG_VIRTIO_REG_##name) = rt_cpu_to_le32(value)
+
+static void rpmsg_virtio_rx_callback(struct rt_mbox_client *client, void *data)
+{
+ rt_ubase_t level;
+ rt_uint16_t idx_inc = 0;
+ rt_uint32_t *event = data;
+ struct rt_virtqueue *vq;
+ struct rt_virtio_device *vdev;
+ struct virtq *rx_virtq, *tx_virtq;
+ struct rpmsg_virtio *rv = rt_container_of(client, struct rpmsg_virtio, mbox_client);
+
+ if (*event & RPMSG_VIRTIO_EVENT_LINK)
+ {
+ rt_ubase_t desc, avail, used;
+
+ if (rv->is_master)
+ {
+ desc = rpmsg_virtio_readl(rv, SLAVE_QUEUE_DESC);
+ avail = rpmsg_virtio_readl(rv, SLAVE_QUEUE_AVAIL);
+ used = rpmsg_virtio_readl(rv, SLAVE_QUEUE_USED);
+ }
+ else
+ {
+ desc = rpmsg_virtio_readl(rv, MASTER_QUEUE_DESC);
+ avail = rpmsg_virtio_readl(rv, MASTER_QUEUE_AVAIL);
+ used = rpmsg_virtio_readl(rv, MASTER_QUEUE_USED);
+ }
+
+ rv->peer_tx_virtq.desc = (void *)rv->shmem + desc;
+ rv->peer_tx_virtq.avail = (void *)rv->shmem + avail;
+ rv->peer_tx_virtq.used = (void *)rv->shmem + used;
+ rv->next_idx = 0;
+ }
+
+ rx_virtq = &rv->rx_virtq;
+ tx_virtq = &rv->peer_tx_virtq;
+ vdev = &rv->parent;
+
+ /* Process local RX queue */
+ while (tx_virtq->avail && rv->next_idx != tx_virtq->avail->idx)
+ {
+ rt_uint16_t tx_idx, rx_idx, tx_used_idx, rx_used_idx;
+ struct virtq_desc *tx_desc, *rx_desc;
+
+ tx_idx = virtio16_to_cpu(vdev, tx_virtq->avail->ring[rv->next_idx % tx_virtq->num]);
+ rx_idx = virtio16_to_cpu(vdev, rx_virtq->avail->ring[rv->next_idx % rx_virtq->num]);
+ tx_used_idx = virtio16_to_cpu(vdev, tx_virtq->used->idx) % tx_virtq->num;
+ rx_used_idx = virtio16_to_cpu(vdev, rx_virtq->used->idx) % rx_virtq->num;
+ rt_hw_rmb();
+
+ tx_desc = &tx_virtq->desc[tx_idx];
+ rx_desc = &rx_virtq->desc[rx_idx];
+
+ *(rpmsg_dma_tag *)(rv->shmem +
+ virtio64_to_cpu(vdev, rx_desc->addr) +
+ virtio32_to_cpu(vdev, rx_desc->len)) = virtio64_to_cpu(vdev, tx_desc->addr);
+
+ tx_virtq->used->ring[tx_used_idx].id = tx_idx;
+ tx_virtq->used->ring[tx_used_idx].len = cpu_to_virtio32(vdev, 0);
+ rx_virtq->used->ring[rx_used_idx].id = rx_idx;
+ rx_virtq->used->ring[rx_used_idx].len = rx_virtq->desc[rx_idx].len;
+
+ ++rx_virtq->used->idx;
+ ++rv->next_idx;
+ ++idx_inc;
+ rt_hw_wmb();
+ }
+
+ if (*event & RPMSG_VIRTIO_EVENT_QUEUE)
+ {
+ level = rt_spin_lock_irqsave(&rv->spinlock);
+
+ rt_list_for_each_entry(vq, &rv->parent.vq_node, list)
+ {
+ rt_virtqueue_isr(vq->index, vq);
+ }
+
+ rt_spin_unlock_irqrestore(&rv->spinlock, level);
+ }
+
+ /* Process peer TX queue */
+ if (idx_inc)
+ {
+ tx_virtq->used->idx += cpu_to_virtio16(vdev, idx_inc);
+ rt_hw_wmb();
+ }
+}
+
+static rt_bool_t rpmsg_virtio_notify(struct rt_virtqueue *vq)
+{
+ rt_uint32_t event;
+ struct rpmsg_virtio *rv = raw_to_rpmsg_virtio(vq->vdev);
+
+ event = RPMSG_VIRTIO_EVENT_QUEUE;
+ rt_mbox_send(rv->chan, &event, RT_WAITING_FOREVER);
+
+ return RT_TRUE;
+}
+
+static rt_err_t rpmsg_virtio_get_status(struct rt_virtio_device *vdev,
+ rt_uint8_t *out_status)
+{
+ struct rpmsg_virtio *rv = raw_to_rpmsg_virtio(vdev);
+
+ *out_status = rv->status;
+
+ return RT_EOK;
+}
+
+static rt_err_t rpmsg_virtio_set_status(struct rt_virtio_device *vdev,
+ rt_uint8_t status)
+{
+ struct rpmsg_virtio *rv = raw_to_rpmsg_virtio(vdev);
+
+ rv->status = status;
+
+ return RT_EOK;
+}
+
+static rt_err_t rpmsg_virtio_get_features(struct rt_virtio_device *vdev,
+ rt_uint64_t *out_features)
+{
+ struct rpmsg_virtio *rv = raw_to_rpmsg_virtio(vdev);
+
+ *out_features = RT_BIT_ULL(VIRTIO_F_VERSION_1) | RT_BIT_ULL(VIRTIO_F_ANY_LAYOUT);
+
+ if (rv->is_master)
+ {
+ *out_features |= RT_BIT_ULL(VIRTIO_RPMSG_F_NS);
+ }
+
+ return RT_EOK;
+}
+
+static rt_err_t rpmsg_virtio_set_features(struct rt_virtio_device *vdev)
+{
+ return RT_EOK;
+}
+
+static rt_err_t rpmsg_virtio_get_config(struct rt_virtio_device *vdev,
+ rt_uint32_t offset, void *dst, int length)
+{
+ return -RT_ENOSYS;
+}
+
+static rt_err_t rpmsg_virtio_set_config(struct rt_virtio_device *vdev,
+ rt_uint32_t offset, const void *src, int length)
+{
+ return -RT_ENOSYS;
+}
+
+static rt_err_t rpmsg_virtio_install_vqs(struct rt_virtio_device *vdev, int vqs_nr,
+ struct rt_virtqueue *vqs[], const char *names[], rt_virtqueue_callback cbs[])
+{
+ rt_uint32_t event;
+ struct rt_virtqueue *vq;
+ struct rpmsg_virtio *rv = raw_to_rpmsg_virtio(vdev);
+
+ if (vqs_nr != 2)
+ {
+ return -RT_EINVAL;
+ }
+
+ /* Master TX or slave RX */
+ vq = rt_virtqueue_create(vdev, names[0], 0, rv->queue_max, ARCH_PAGE_SIZE,
+ rpmsg_virtio_notify, cbs[0], RT_NULL);
+
+ if (!vq)
+ {
+ goto _fail;
+ }
+ vqs[0] = vq;
+
+ /* Master RX or slave TX */
+ vq = rt_virtqueue_create(vdev, names[1], 1, rv->queue_max, ARCH_PAGE_SIZE,
+ rpmsg_virtio_notify, cbs[1], RT_NULL);
+
+ if (!vq)
+ {
+ goto _fail;
+ }
+ vqs[1] = vq;
+
+ if (rv->is_master)
+ {
+ rv->rx_virtq.desc = (void *)rt_virtqueue_get_desc_addr(vqs[1]);
+ rv->rx_virtq.avail = (void *)rt_virtqueue_get_avail_addr(vqs[1]);
+ rv->rx_virtq.used = (void *)rt_virtqueue_get_used_addr(vqs[1]);
+
+ rpmsg_virtio_writel(rv, MASTER_QUEUE_DESC,
+ (rt_ubase_t)rt_virtqueue_get_desc_addr(vqs[0]) - (rt_ubase_t)rv->shmem);
+ rpmsg_virtio_writel(rv, MASTER_QUEUE_AVAIL,
+ (rt_ubase_t)rt_virtqueue_get_avail_addr(vqs[0]) - (rt_ubase_t)rv->shmem);
+ rpmsg_virtio_writel(rv, MASTER_QUEUE_USED,
+ (rt_ubase_t)rt_virtqueue_get_used_addr(vqs[0]) - (rt_ubase_t)rv->shmem);
+ }
+ else
+ {
+ rv->rx_virtq.desc = (void *)rt_virtqueue_get_desc_addr(vqs[0]);
+ rv->rx_virtq.avail = (void *)rt_virtqueue_get_avail_addr(vqs[0]);
+ rv->rx_virtq.used = (void *)rt_virtqueue_get_used_addr(vqs[0]);
+
+ rpmsg_virtio_writel(rv, SLAVE_QUEUE_DESC,
+ (rt_ubase_t)rt_virtqueue_get_desc_addr(vqs[1]) - (rt_ubase_t)rv->shmem);
+ rpmsg_virtio_writel(rv, SLAVE_QUEUE_AVAIL,
+ (rt_ubase_t)rt_virtqueue_get_avail_addr(vqs[1]) - (rt_ubase_t)rv->shmem);
+ rpmsg_virtio_writel(rv, SLAVE_QUEUE_USED,
+ (rt_ubase_t)rt_virtqueue_get_used_addr(vqs[1]) - (rt_ubase_t)rv->shmem);
+
+ rv->peer_tx_virtq.desc = (void *)rv->shmem + rpmsg_virtio_readl(rv, MASTER_QUEUE_DESC);
+ rv->peer_tx_virtq.avail = (void *)rv->shmem + rpmsg_virtio_readl(rv, MASTER_QUEUE_AVAIL);
+ rv->peer_tx_virtq.used = (void *)rv->shmem + rpmsg_virtio_readl(rv, MASTER_QUEUE_USED);
+ }
+ rv->rx_virtq.num = rv->queue_max;
+ rv->peer_tx_virtq.num = rv->queue_max;
+
+ /* Ask peer to link */
+ event = RPMSG_VIRTIO_EVENT_LINK;
+ rt_mbox_send(rv->chan, &event, RT_WAITING_FOREVER);
+
+ return RT_EOK;
+
+_fail:
+ rt_virtio_virtqueue_release(vdev);
+
+ return -RT_ERROR;
+}
+
+static rt_err_t rpmsg_virtio_release_vqs(struct rt_virtio_device *vdev)
+{
+ struct rt_virtqueue *vq, *vq_next;
+ struct rpmsg_virtio *rv = raw_to_rpmsg_virtio(vdev);
+
+ rt_list_for_each_entry_safe(vq, vq_next, &rv->parent.vq_node, list)
+ {
+ rt_virtqueue_delete(&rv->parent, vq);
+ }
+
+ return RT_EOK;
+}
+
+static rt_err_t rpmsg_virtio_control_vqs(struct rt_virtio_device *vdev,
+ rt_uint32_t cfg, void *data)
+{
+ return -RT_ENOSYS;
+}
+
+static rt_err_t rpmsg_virtio_generation(struct rt_virtio_device *vdev,
+ rt_uint32_t *out_counter)
+{
+ return RT_EOK;
+}
+
+static rt_err_t rpmsg_virtio_reset(struct rt_virtio_device *vdev)
+{
+ struct rpmsg_virtio *rv = raw_to_rpmsg_virtio(vdev);
+
+ rv->status = 0;
+
+ return RT_EOK;
+}
+
+static const struct rt_virtio_transport rpmsg_virtio_trans =
+{
+ .get_status = rpmsg_virtio_get_status,
+ .set_status = rpmsg_virtio_set_status,
+ .get_features = rpmsg_virtio_get_features,
+ .set_features = rpmsg_virtio_set_features,
+ .get_config = rpmsg_virtio_get_config,
+ .set_config = rpmsg_virtio_set_config,
+ .install_vqs = rpmsg_virtio_install_vqs,
+ .release_vqs = rpmsg_virtio_release_vqs,
+ .control_vqs = rpmsg_virtio_control_vqs,
+ .generation = rpmsg_virtio_generation,
+ .reset = rpmsg_virtio_reset,
+};
+
+static void *rpmsg_virtio_dma_alloc(struct rt_device *dev, rt_size_t size,
+ rt_ubase_t *dma_handle, rt_ubase_t flags)
+{
+ void *dma_buffer;
+ struct rt_virtio_device *vdev = rt_container_of(dev, struct rt_virtio_device, parent);
+ struct rpmsg_virtio *rv = rt_container_of(vdev, struct rpmsg_virtio, parent);
+
+ /* Ignore flags */
+ if (!(dma_buffer = rt_slab_alloc(rv->slab, size)))
+ {
+ return dma_buffer;
+ }
+
+ if (dma_handle)
+ {
+ *dma_handle = (rt_ubase_t)(dma_buffer - rv->shmem);
+ }
+
+ return dma_buffer;
+}
+
+static void rpmsg_virtio_dma_free(struct rt_device *dev, rt_size_t size,
+ void *cpu_addr, rt_ubase_t dma_handle, rt_ubase_t flags)
+{
+ struct rt_virtio_device *vdev = rt_container_of(dev, struct rt_virtio_device, parent);
+ struct rpmsg_virtio *rv = rt_container_of(vdev, struct rpmsg_virtio, parent);
+
+ rt_slab_free(rv->slab, cpu_addr);
+}
+
+static rt_err_t rpmsg_virtio_dma_sync_out_data(struct rt_device *dev,
+ void *data, rt_size_t size, rt_ubase_t *dma_handle, rt_ubase_t flags)
+{
+ void *dma_buffer;
+
+ dma_buffer = rpmsg_virtio_dma_alloc(dev, size + sizeof(rpmsg_dma_tag), dma_handle, flags);
+
+ if (!dma_buffer)
+ {
+ return -RT_ENOMEM;
+ }
+
+ rt_memcpy(dma_buffer, data, size);
+
+ return RT_EOK;
+}
+
+static rt_err_t rpmsg_virtio_dma_sync_in_data(struct rt_device *dev,
+ void *out_data, rt_size_t size, rt_ubase_t dma_handle, rt_ubase_t flags)
+{
+ void *dma_buffer;
+ rpmsg_dma_tag dma_tag;
+ struct rt_virtio_device *vdev = rt_container_of(dev, struct rt_virtio_device, parent);
+ struct rpmsg_virtio *rv = rt_container_of(vdev, struct rpmsg_virtio, parent);
+
+ dma_buffer = rv->shmem + dma_handle;
+ dma_tag = *(rpmsg_dma_tag *)(dma_buffer + size);
+
+ rt_memcpy(out_data, rv->shmem + dma_tag, size);
+
+ size += sizeof(rpmsg_dma_tag);
+ rpmsg_virtio_dma_free(dev, size, dma_buffer, dma_handle, flags);
+
+ return RT_EOK;
+}
+
+static const struct rt_dma_map_ops rpmsg_virtio_dma_ops =
+{
+ .alloc = rpmsg_virtio_dma_alloc,
+ .free = rpmsg_virtio_dma_free,
+ .sync_out_data = rpmsg_virtio_dma_sync_out_data,
+ .sync_in_data = rpmsg_virtio_dma_sync_in_data,
+};
+
+static rt_err_t rpmsg_virtio_probe(struct rt_platform_device *pdev)
+{
+ rt_err_t err;
+ rt_uint64_t addr, size;
+ struct rt_ofw_node *shmem_np;
+ struct rt_virtio_device *vdev;
+ struct rt_device *dev = &pdev->parent;
+ struct rpmsg_virtio *rv = rt_calloc(1, sizeof(*rv));
+
+ if (!rv)
+ {
+ return -RT_ENOMEM;
+ }
+
+ rv->mbox_client.dev = dev;
+ rv->mbox_client.rx_callback = rpmsg_virtio_rx_callback;
+
+ rv->chan = rt_mbox_request_by_index(&rv->mbox_client, 0);
+
+ if (rt_is_err_or_null(rv->chan))
+ {
+ err = -RT_EINVAL;
+ LOG_E("Request mailbox fail");
+
+ goto _fail;
+ }
+
+ vdev = &rv->parent;
+
+ if ((err = rt_dm_dev_prop_read_u32(dev, "queue-max", &rv->queue_max)))
+ {
+ goto _fail;
+ }
+
+ shmem_np = rt_ofw_parse_phandle(dev->ofw_node, "shmem", 0);
+ rt_ofw_get_address(shmem_np, 0, &addr, &size);
+ rt_ofw_node_put(shmem_np);
+
+ rv->shmem = rt_ioremap_cached((void *)addr, size);
+
+ if (!rv->shmem)
+ {
+ err = -RT_EIO;
+ goto _fail;
+ }
+
+ rv->is_master = rt_dm_dev_prop_read_bool(dev, "master");
+ rv->regs = rv->shmem;
+ rv->pool = rv->shmem + ARCH_PAGE_SIZE;
+
+ size = (size - ARCH_PAGE_SIZE) >> 1;
+
+ if (!rv->is_master)
+ {
+ rv->pool += size;
+ }
+
+ if (!(rv->slab = rt_slab_init(rt_dm_dev_get_name(dev), rv->pool, size)))
+ {
+ LOG_E("Create AMP dma pool fail");
+
+ err = -RT_ENOMEM;
+ goto _fail;
+ }
+ rt_dma_device_set_ops(&vdev->parent, &rpmsg_virtio_dma_ops);
+ vdev->dma_dispatch = RT_TRUE;
+
+ rt_spin_lock_init(&rv->spinlock);
+
+ vdev->id.device = VIRTIO_DEVICE_ID_RPMSG;
+ vdev->id.vendor = 0x534f5452; /* RTOS */
+ vdev->trans = &rpmsg_virtio_trans;
+ vdev->parent.ofw_node = dev->ofw_node;
+
+ if ((err = rt_virtio_device_register(vdev)))
+ {
+ goto _fail;
+ }
+
+ dev->user_data = rv;
+
+ return RT_EOK;
+
+_fail:
+ if (!rt_is_err_or_null(rv->chan))
+ {
+ rt_mbox_release(rv->chan);
+ }
+ if (rv->shmem)
+ {
+ rt_iounmap(rv->shmem);
+ }
+ rt_free(rv);
+
+ return err;
+}
+
+static rt_err_t rpmsg_virtio_remove(struct rt_platform_device *pdev)
+{
+ struct rpmsg_virtio *rv = pdev->parent.user_data;
+
+ rt_bus_remove_device(&rv->parent.parent);
+
+ rt_slab_detach(rv->slab);
+ rt_mbox_release(rv->chan);
+ rt_iounmap(rv->shmem);
+ rt_free(rv);
+
+ return RT_EOK;
+}
+
+static const struct rt_ofw_node_id rpmsg_virtio_ofw_ids[] =
+{
+ { .compatible = "rt-thread,virtio-rpmsg" },
+ { /* sentinel */ }
+};
+
+static struct rt_platform_driver rpmsg_virtio_driver =
+{
+ .name = "rt-thread-virtio-rpmsg",
+ .ids = rpmsg_virtio_ofw_ids,
+
+ .probe = rpmsg_virtio_probe,
+ .remove = rpmsg_virtio_remove,
+};
+RT_PLATFORM_DRIVER_EXPORT(rpmsg_virtio_driver);