summaryrefslogtreecommitdiff
path: root/components/drivers
diff options
context:
space:
mode:
authorguo <[email protected]>2022-12-03 12:07:44 +0800
committerGitHub <[email protected]>2022-12-03 12:07:44 +0800
commitecf2d8215961ca7c539132b62a72df6072cc3e14 (patch)
tree59005f0f74f2593c0435059264f624c862a89f55 /components/drivers
parentaaf5462c6d4ae9466a0a8cd2a4bb00cfe1fef2f2 (diff)
sync branch rt-smart. (#6641)
* Synchronize the code of the rt mart branch to the master branch. * TTY device * Add lwP code from rt-smart * Add vnode in DFS, but DFS will be re-write for rt-smart * There are three libcpu for rt-smart: * arm/cortex-a, arm/aarch64 * riscv64 Co-authored-by: Rbb666 <[email protected]> Co-authored-by: zhkag <[email protected]>
Diffstat (limited to 'components/drivers')
-rwxr-xr-xcomponents/drivers/Kconfig108
-rw-r--r--components/drivers/cputime/SConscript3
-rw-r--r--components/drivers/cputime/cputime.c8
-rw-r--r--components/drivers/cputime/cputime_riscv.c39
-rw-r--r--components/drivers/fdt/README.md41
-rw-r--r--components/drivers/fdt/SConscript16
-rw-r--r--components/drivers/fdt/docs/README.md13
-rw-r--r--components/drivers/fdt/docs/api.md434
-rw-r--r--components/drivers/fdt/docs/examples.md81
-rw-r--r--components/drivers/fdt/docs/version.md7
-rw-r--r--components/drivers/fdt/examples/SConscript10
-rw-r--r--components/drivers/fdt/examples/fdt_test.c143
-rw-r--r--components/drivers/fdt/inc/dtb_node.h379
-rw-r--r--components/drivers/fdt/libfdt/SConscript10
-rw-r--r--components/drivers/fdt/libfdt/fdt.c249
-rw-r--r--components/drivers/fdt/libfdt/fdt.h111
-rw-r--r--components/drivers/fdt/libfdt/fdt_addresses.c99
-rw-r--r--components/drivers/fdt/libfdt/fdt_empty_tree.c84
-rw-r--r--components/drivers/fdt/libfdt/fdt_ro.c701
-rw-r--r--components/drivers/fdt/libfdt/fdt_rw.c489
-rw-r--r--components/drivers/fdt/libfdt/fdt_strerror.c100
-rw-r--r--components/drivers/fdt/libfdt/fdt_sw.c286
-rw-r--r--components/drivers/fdt/libfdt/fdt_wip.c137
-rw-r--r--components/drivers/fdt/libfdt/libfdt.h1876
-rw-r--r--components/drivers/fdt/libfdt/libfdt_env.h113
-rw-r--r--components/drivers/fdt/libfdt/libfdt_internal.h95
-rw-r--r--components/drivers/fdt/src/SConscript17
-rw-r--r--components/drivers/fdt/src/dtb_access.c650
-rw-r--r--components/drivers/fdt/src/dtb_addr.c57
-rw-r--r--components/drivers/fdt/src/dtb_base.c537
-rw-r--r--components/drivers/fdt/src/dtb_get.c817
-rw-r--r--components/drivers/fdt/src/dtb_head.c41
-rw-r--r--components/drivers/fdt/src/dtb_load.c105
-rw-r--r--components/drivers/fdt/src/dtb_set.c166
-rw-r--r--components/drivers/include/drivers/cputime.h4
-rw-r--r--components/drivers/include/drivers/gpt.h132
-rw-r--r--components/drivers/include/drivers/lcd.h111
-rw-r--r--components/drivers/include/drivers/mmcsd_card.h4
-rw-r--r--components/drivers/include/drivers/mmcsd_cmd.h3
-rw-r--r--components/drivers/include/drivers/mmcsd_core.h23
-rw-r--r--components/drivers/include/drivers/mmcsd_host.h53
-rw-r--r--components/drivers/include/drivers/rt_dev_bus.h18
-rw-r--r--components/drivers/include/drivers/serial.h2
-rw-r--r--components/drivers/include/drivers/serial_v2.h2
-rw-r--r--components/drivers/include/ipc/pipe.h8
-rw-r--r--components/drivers/include/ipc/waitqueue.h2
-rw-r--r--components/drivers/include/rtdevice.h4
-rw-r--r--components/drivers/ipc/completion.c5
-rw-r--r--components/drivers/ipc/dataqueue.c4
-rw-r--r--components/drivers/ipc/pipe.c283
-rw-r--r--components/drivers/ipc/waitqueue.c26
-rw-r--r--components/drivers/ipc/workqueue.c8
-rw-r--r--components/drivers/misc/SConscript12
-rw-r--r--components/drivers/misc/rt_dev_bus.c80
-rw-r--r--components/drivers/misc/rt_null.c74
-rw-r--r--components/drivers/misc/rt_random.c159
-rw-r--r--components/drivers/misc/rt_zero.c75
-rw-r--r--components/drivers/sdio/SConscript1
-rw-r--r--components/drivers/sdio/block_dev.c408
-rw-r--r--components/drivers/sdio/gpt.c561
-rw-r--r--components/drivers/sdio/mmc.c634
-rw-r--r--components/drivers/sdio/mmcsd_core.c88
-rw-r--r--components/drivers/sdio/sd.c1
-rw-r--r--components/drivers/sdio/sdio.c6
-rw-r--r--components/drivers/serial/serial.c43
-rw-r--r--components/drivers/serial/serial_v2.c31
-rw-r--r--components/drivers/tty/SConscript10
-rw-r--r--components/drivers/tty/console.c325
-rw-r--r--components/drivers/tty/include/console.h19
-rw-r--r--components/drivers/tty/include/tty.h490
-rw-r--r--components/drivers/tty/include/tty_ldisc.h56
-rw-r--r--components/drivers/tty/n_tty.c2041
-rw-r--r--components/drivers/tty/pty.c374
-rw-r--r--components/drivers/tty/tty.c424
-rw-r--r--components/drivers/tty/tty_ioctl.c109
-rw-r--r--components/drivers/tty/tty_ldisc.c181
-rw-r--r--components/drivers/virtio/SConscript11
-rw-r--r--components/drivers/virtio/virtio.c324
-rw-r--r--components/drivers/virtio/virtio.h156
-rw-r--r--components/drivers/virtio/virtio_blk.c253
-rw-r--r--components/drivers/virtio/virtio_blk.h105
-rw-r--r--components/drivers/virtio/virtio_console.c778
-rw-r--r--components/drivers/virtio/virtio_console.h97
-rw-r--r--components/drivers/virtio/virtio_gpu.c934
-rw-r--r--components/drivers/virtio/virtio_gpu.h412
-rw-r--r--components/drivers/virtio/virtio_input.c449
-rw-r--r--components/drivers/virtio/virtio_input.h145
-rw-r--r--components/drivers/virtio/virtio_input_event_codes.h932
-rw-r--r--components/drivers/virtio/virtio_mmio.h67
-rw-r--r--components/drivers/virtio/virtio_net.c314
-rw-r--r--components/drivers/virtio/virtio_net.h119
-rw-r--r--components/drivers/virtio/virtio_queue.h97
92 files changed, 19490 insertions, 619 deletions
diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig
index af4132f4e2..da0211683f 100755
--- a/components/drivers/Kconfig
+++ b/components/drivers/Kconfig
@@ -4,6 +4,10 @@ config RT_USING_DEVICE_IPC
bool "Using device drivers IPC"
default y
+config RT_UNAMED_PIPE_NUMBER
+ int "The number of unamed pipe"
+ default 64
+
if RT_USING_DEVICE_IPC
config RT_USING_SYSTEM_WORKQUEUE
bool "Using system default workqueue"
@@ -45,6 +49,17 @@ menuconfig RT_USING_SERIAL
default 64
endif
+config RT_USING_TTY
+ bool "Using TTY SYSTEM"
+ depends on RT_USING_LWP
+ default y
+
+if RT_USING_TTY
+ config RT_TTY_DEBUG
+ bool "Using TTY DEBUG"
+ default n
+endif
+
config RT_USING_CAN
bool "Using CAN device drivers"
default n
@@ -86,6 +101,12 @@ if RT_USING_CPUTIME
default y
depends on ARCH_ARM_CORTEX_M0 || ARCH_ARM_CORTEX_M3 || ARCH_ARM_CORTEX_M4 || ARCH_ARM_CORTEX_M7
select PKG_USING_PERF_COUNTER
+ config RT_USING_CPUTIME_RISCV
+ bool "Use rdtime instructions for CPU time"
+ default y
+ depends on ARCH_RISCV64
+ help
+ Some RISCV64 MCU Use rdtime instructions read CPU time.
endif
config RT_USING_I2C
@@ -123,7 +144,19 @@ config RT_USING_ADC
config RT_USING_DAC
bool "Using DAC device drivers"
default n
-
+
+config RT_USING_NULL
+ bool "Using NULL device drivers"
+ default n
+
+config RT_USING_ZERO
+ bool "Using ZERO device drivers"
+ default n
+
+config RT_USING_RANDOM
+ bool "Using RANDOM device drivers"
+ default n
+
config RT_USING_PWM
bool "Using PWM device drivers"
default n
@@ -182,6 +215,19 @@ config RT_USING_PM
endif
endif
+config RT_USING_FDT
+ bool "Using fdt interface for device drivers"
+ default n
+ if RT_USING_FDT
+ config RT_USING_FDTLIB
+ bool "Using fdt lib for device drivers"
+ default y
+ config FDT_USING_DEBUG
+ bool "Using fdt debug function "
+ default n
+
+ endif
+
config RT_USING_RTC
bool "Using RTC device drivers"
default n
@@ -322,13 +368,8 @@ config RT_USING_SENSOR
if RT_USING_SENSOR
config RT_USING_SENSOR_CMD
- bool "Enable sensor commands"
- select PKG_USING_RT_VSNPRINTF_FULL
+ bool "Using Sensor cmd"
default y
-
- config RT_USING_SENSOR_DOUBLE_FLOAT
- bool "Using double floating as sensor data type"
- default n
endif
config RT_USING_TOUCH
@@ -340,6 +381,10 @@ config RT_USING_TOUCH
default n
endif
+config RT_USING_LCD
+ bool "Using LCD graphic drivers"
+ default n
+
menuconfig RT_USING_HWCRYPTO
bool "Using Hardware Crypto drivers"
default n
@@ -520,6 +565,11 @@ if RT_USING_INPUT_CAPTURE
default 100
endif
+config RT_USING_DEV_BUS
+ bool "Using Device Bus device drivers"
+ default y if RT_USING_LWP
+ default n if !RT_USING_LWP
+
menuconfig RT_USING_WIFI
bool "Using Wi-Fi framework"
default n
@@ -671,6 +721,50 @@ menuconfig RT_USING_WIFI
endif
endif
+menuconfig RT_USING_VIRTIO
+ bool "Using VirtIO device drivers"
+ default n
+
+ if RT_USING_VIRTIO
+ choice
+ prompt "VirtIO Version"
+ default RT_USING_VIRTIO10
+
+ config RT_USING_VIRTIO10
+ bool "VirtIO v1.0"
+ endchoice
+
+ config RT_USING_VIRTIO_MMIO_ALIGN
+ bool "Using VirtIO MMIO alignment"
+ default y
+
+ config RT_USING_VIRTIO_BLK
+ bool "Using VirtIO BLK"
+ default y
+
+ config RT_USING_VIRTIO_NET
+ bool "Using VirtIO NET"
+ default y
+
+ menuconfig RT_USING_VIRTIO_CONSOLE
+ bool "Using VirtIO Console"
+ default y
+
+ if RT_USING_VIRTIO_CONSOLE
+ config RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR
+ int "Max number of port in VirtIO Console"
+ default 4
+ endif
+
+ config RT_USING_VIRTIO_GPU
+ bool "Using VirtIO GPU"
+ default y
+
+ config RT_USING_VIRTIO_INPUT
+ bool "Using VirtIO Input"
+ default y
+ endif
+
menu "Using USB"
config RT_USING_USB
bool
diff --git a/components/drivers/cputime/SConscript b/components/drivers/cputime/SConscript
index 57c0b44fa8..e1b447e16b 100644
--- a/components/drivers/cputime/SConscript
+++ b/components/drivers/cputime/SConscript
@@ -9,6 +9,9 @@ cputime.c
if GetDepend('RT_USING_CPUTIME_CORTEXM'):
src += ['cputime_cortexm.c']
+if GetDepend('RT_USING_CPUTIME_RISCV'):
+ src += ['cputime_riscv.c']
+
group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_CPUTIME'], CPPPATH = CPPPATH)
Return('group')
diff --git a/components/drivers/cputime/cputime.c b/components/drivers/cputime/cputime.c
index 490f5725c1..5fe0b07cb1 100644
--- a/components/drivers/cputime/cputime.c
+++ b/components/drivers/cputime/cputime.c
@@ -51,11 +51,11 @@ uint64_t clock_cpu_gettime(void)
*
* @return the microsecond
*/
-uint32_t clock_cpu_microsecond(uint32_t cpu_tick)
+uint64_t clock_cpu_microsecond(uint64_t cpu_tick)
{
float unit = clock_cpu_getres();
- return (uint32_t)((cpu_tick * unit) / 1000);
+ return (uint64_t)((cpu_tick * unit) / 1000);
}
/**
@@ -66,11 +66,11 @@ uint32_t clock_cpu_microsecond(uint32_t cpu_tick)
*
* @return the millisecond
*/
-uint32_t clock_cpu_millisecond(uint32_t cpu_tick)
+uint64_t clock_cpu_millisecond(uint64_t cpu_tick)
{
float unit = clock_cpu_getres();
- return (uint32_t)((cpu_tick * unit) / (1000 * 1000));
+ return (uint64_t)((cpu_tick * unit) / (1000 * 1000));
}
/**
diff --git a/components/drivers/cputime/cputime_riscv.c b/components/drivers/cputime/cputime_riscv.c
new file mode 100644
index 0000000000..7e327fc2d4
--- /dev/null
+++ b/components/drivers/cputime/cputime_riscv.c
@@ -0,0 +1,39 @@
+#include <rthw.h>
+#include <rtdevice.h>
+#include <rtthread.h>
+
+#include <board.h>
+
+
+#define TIMER_FREQ (24000000)
+
+/* Use Cycle counter of Data Watchpoint and Trace Register for CPU time */
+
+static float riscv_cputime_getres(void)
+{
+ float ret = 1000 * 1000 * 1000;
+
+ ret = ret / TIMER_FREQ;
+ return ret;
+}
+
+static uint64_t riscv_cputime_gettime(void)
+{
+ uint64_t time_elapsed;
+ __asm__ __volatile__(
+ "rdtime %0"
+ : "=r"(time_elapsed));
+ return time_elapsed;
+}
+
+const static struct rt_clock_cputime_ops _riscv_ops =
+ {
+ riscv_cputime_getres,
+ riscv_cputime_gettime};
+
+int riscv_cputime_init(void)
+{
+ clock_cpu_setops(&_riscv_ops);
+ return 0;
+}
+INIT_BOARD_EXPORT(riscv_cputime_init);
diff --git a/components/drivers/fdt/README.md b/components/drivers/fdt/README.md
new file mode 100644
index 0000000000..d8a049c8fc
--- /dev/null
+++ b/components/drivers/fdt/README.md
@@ -0,0 +1,41 @@
+# fdt
+
+## 1、介绍
+fdt基于libfdt进行封装,可实现在内存或文件系统中加载dtb设备树,对内存中的设备树修改、解析,并转换为设备节点树,通过该节点树开发者可通过设备树信息开发驱动。
+
+### 1.1 目录结构
+| 名称 | 说明 |
+| ---- | ---- |
+| docs | 文档目录 |
+| examples | 例子目录,并有相应的一些说明 |
+| inc | 头文件目录 |
+| src | 源代码目录 |
+
+### 1.2 许可证
+fdt package 遵循 GPL-3.0 许可,详见 LICENSE 文件。
+
+### 1.3 依赖
+- RT-Thread 3.0+
+
+## 2、如何打开 fdt
+使用 fdt package 需要在 RT-Thread 的包管理器中选择它,具体路径如下:
+
+```
+RT-Thread online packages
+ tools packages --->
+ [*] Device Tree package in RT-Thread
+```
+
+## 3、使用 fdt
+在打开 fdt package 后,当进行 bsp 编译时,它会被加入到 bsp 工程中进行编译。
+
+* 完整的 API 手册可以访问这个[链接](docs/api.md)
+* 更多文档位于 [`/docs`](/docs) 下,使用前 **务必查看**
+
+## 4、注意事项
+如果发生`libfdt`库冲突,在package管理菜单中取消选择`Enable libfdt`
+
+## 5、联系方式
+
+* 维护:GuEe-GUI
+* 主页:https://github.com/GuEe-GUI/fdt \ No newline at end of file
diff --git a/components/drivers/fdt/SConscript b/components/drivers/fdt/SConscript
new file mode 100644
index 0000000000..d916b2dfbc
--- /dev/null
+++ b/components/drivers/fdt/SConscript
@@ -0,0 +1,16 @@
+# RT-Thread building script for bridge
+
+import os
+from building import *
+
+cwd = GetCurrentDir()
+objs = []
+list = os.listdir(cwd)
+
+if GetDepend('RT_USING_FDT'):
+ for d in list:
+ path = os.path.join(cwd, d)
+ if os.path.isfile(os.path.join(path, 'SConscript')):
+ objs = objs + SConscript(os.path.join(d, 'SConscript'))
+
+Return('objs')
diff --git a/components/drivers/fdt/docs/README.md b/components/drivers/fdt/docs/README.md
new file mode 100644
index 0000000000..f57e0a8e75
--- /dev/null
+++ b/components/drivers/fdt/docs/README.md
@@ -0,0 +1,13 @@
+# 文档
+
+## 软件包地址
+
+- https://gitee.com/GuEe_GUI/fdt
+
+## 文档列表
+
+|文件名 |描述|
+|:----- |:----|
+|[examples.md](examples.md) |示例程序|
+|[version.md](version.md) |版本信息|
+|[api.md](api.md) |API 说明|
diff --git a/components/drivers/fdt/docs/api.md b/components/drivers/fdt/docs/api.md
new file mode 100644
index 0000000000..13471de06f
--- /dev/null
+++ b/components/drivers/fdt/docs/api.md
@@ -0,0 +1,434 @@
+# fdt load API
+
+## 从文件系统上读取设备树
+```c
+void *fdt_load_from_fs(char *dtb_filename)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|dtb_filename | 设备树文件名 |
+| **返回** | **描述** |
+|void* | 设备树在内存上的地址 |
+
+## 从内存上读取设备树
+```c
+void *fdt_load_from_memory(void *dtb_ptr, rt_bool_t is_clone)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|dtb_ptr | 设备树在内存上的内存地址 |
+|is_clone | 是否克隆到新的内存区域 |
+| **返回** | **描述** |
+|void* | 设备树在内存上的地址 |
+
+# fdt set API
+
+## 设置Linux启动参数
+```c
+rt_size_t fdt_set_linux_cmdline(void *fdt, char *cmdline)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|fdt | 设备树指针 |
+|cmdline | 启动参数 |
+| **返回** | **描述** |
+|rt_size_t | 修改设备树后设备树的大小 |
+
+## 设置Linux init ramdisk
+```c
+rt_size_t fdt_set_linux_initrd(void *fdt, rt_uint64_t initrd_addr, rt_size_t initrd_size)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|fdt | 设备树指针 |
+|initrd_addr | init ramdisk 内存地址 |
+|initrd_size | init 大小 |
+| **返回** | **描述** |
+|rt_size_t | 修改设备树后设备树的大小 |
+
+## 设置节点属性值
+```c
+rt_size_t fdt_set_dtb_property(void *fdt, char *pathname, char *property_name, rt_uint32_t *cells, rt_size_t cells_size);
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|fdt | 设备树指针 |
+|pathname | 节点路径 |
+|property_name | 属性名称 |
+|cells | 属性值地址 |
+|cells_size | 属性值长度 |
+| **返回** | **描述** |
+|rt_size_t | 修改设备树后设备树的大小 |
+
+## 添加保留内存
+```c
+rt_size_t fdt_add_dtb_memreserve(void *fdt, rt_uint64_t address, rt_uint64_t size)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|fdt | 设备树指针 |
+|address | 保留内存起始地址 |
+|size | 保留内存大小 |
+| **返回** | **描述** |
+|rt_size_t | 修改设备树后设备树的大小 |
+
+## 删除保留内存
+```c
+rt_size_t fdt_del_dtb_memreserve(void *fdt, rt_uint64_t address)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|fdt | 设备树指针 |
+|address | 保留内存起始地址 |
+| **返回** | **描述** |
+|rt_size_t | 修改设备树后设备树的大小 |
+
+# fdt get API
+
+## 获取设备树软件包执行状态
+```c
+rt_err_t fdt_get_exec_status()
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|无 | 无参数 |
+| **返回** | **描述** |
+|FDT_RET_NO_MEMORY | 内存不足 |
+|FDT_RET_NO_LOADED | 设备树还未加载进内存 |
+|FDT_RET_GET_OK | 执行成功 |
+|FDT_RET_GET_EMPTY | 读取数据为空 |
+
+## 将原始设备树转换为设备节点树
+```c
+struct dtb_node *fdt_get_dtb_list(void *fdt)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|fdt | 设备树在内存上的地址 |
+| **返回** | **描述** |
+|struct dtb_node * | 设备节点树头指针 |
+## 释放设备节点树内存
+```c
+void fdt_free_dtb_list(struct dtb_node *dtb_node_head)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|dtb_node_head | 设备节点树头节点 |
+| **返回** | **描述** |
+|无返回值 | 无描述 |
+
+示例:加载设备树
+
+```c
+#include <rtthread.h>
+#include <fdt.h>
+
+int main()
+{
+ void *fdt = fdt_load_from_fs("sample.dtb");
+ if (fdt != RT_NULL)
+ {
+ struct dtb_node *dtb_node_list = fdt_get_dtb_list(fdt);
+
+ if (dtb_node_list != RT_NULL)
+ {
+ /* load dtb node list OK */
+
+ /* free dtb_node_list here */
+ fdt_free_dtb_list(dtb_node_list);
+ }
+
+ rt_free(fdt);
+ }
+
+ return 0;
+}
+```
+
+## 打印设备树信息
+```c
+void fdt_get_dts_dump(struct dtb_node *dtb_node_head)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|dtb_node_head | 设备节点树头节点 |
+| **返回** | **描述** |
+|无返回值 | 无描述 |
+
+## 遍历设备节点树并使用程序定义的回调函数
+```c
+void fdt_get_enum_dtb_node(struct dtb_node *dtb_node_head, void (*callback(struct dtb_node *dtb_node)))
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|dtb_node_head | 设备节点树头节点 |
+|callback | 程序定义的回调函数 |
+| **返回** | **描述** |
+|无返回值 | 无描述 |
+
+示例:遍历设备树节点,并打印每个节点名称
+
+```c
+#include <rtthread.h>
+#include <fdt.h>
+
+void callback(struct dtb_node *node)
+{
+ rt_kprintf("this node's name is %s\n", node->name);
+}
+
+int main()
+{
+ /* loaded dtb_node */
+ extern struct dtb_node *dtb_node_list;
+
+ if (dtb_node_list != RT_NULL)
+ {
+ fdt_get_enum_dtb_node(dtb_node_list, callback);
+ }
+
+ return 0;
+}
+```
+
+## 通过节点名称查找节点
+```c
+struct dtb_node *fdt_get_dtb_node_by_name_DFS(struct dtb_node *dtb_node, const char *nodename)
+```
+```c
+struct dtb_node *fdt_get_dtb_node_by_name_BFS(struct dtb_node *dtb_node, const char *nodename)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|dtb_node | 设备节点树节点 |
+|nodename | 查找节点的名称 |
+| **返回** | **描述** |
+|struct dtb_node * | 返回查找的节点 |
+|RT_NULL | 未找到为RT_NULL |
+
+## 通过节点路径查找节点
+```c
+struct dtb_node *fdt_get_dtb_node_by_path(struct dtb_node *dtb_node, const char *pathname)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|dtb_node | 设备节点树节点 |
+|pathname | 查找节点的路径 |
+| **返回** | **描述** |
+|struct dtb_node * | 返回查找的节点 |
+|RT_NULL | 未找到为RT_NULL |
+
+## 通过节点phandle值查找节点
+```c
+struct dtb_node *fdt_get_dtb_node_by_phandle_DFS(struct dtb_node *dtb_node, phandle handle)
+```
+```c
+struct dtb_node *fdt_get_dtb_node_by_phandle_BFS(struct dtb_node *dtb_node, phandle handle)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|dtb_node | 设备节点树节点 |
+|handle | 查找节点的phandle值 |
+| **返回** | **描述** |
+|struct dtb_node * | 返回查找的节点 |
+|RT_NULL | 未找到为RT_NULL |
+
+DFS和BFS是用于区分不同深度节点的搜索方法,时间复杂度和空间复杂度都较高,建议使用路径查找。
+
+## 读取节点属性值的单位
+```c
+void fdt_get_dtb_node_cells(struct dtb_node *dtb_node, int *addr_cells, int *size_cells)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|dtb_node | 设备节点树节点 |
+|addr_cells | 返回的地址块的单位(u32)大小 |
+|size_cells | 返回的尺寸块的单位(u32)大小 |
+| **返回** | **描述** |
+|无返回值 | 无描述 |
+
+
+## 读取节点属性值
+```c
+void *fdt_get_dtb_node_property(struct dtb_node *dtb_node, const char *property_name, rt_size_t *property_size)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|dtb_node | 设备节点树节点 |
+|property_name | 属性名称 |
+|property_size | 属性大小 |
+| **返回** | **描述** |
+|void * | 无描述 |
+|RT_NULL | 该设备树没有该属性 |
+
+读取的值为在设备树中存储的值,CPU小端模式下可能需要使用其他API进行转换才是正确的值
+
+## 读取预留内存信息
+```c
+struct dtb_memreserve *fdt_get_dtb_memreserve(struct dtb_node *dtb_node, int *memreserve_size)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|dtb_node | 设备节点树节点 |
+|memreserve_size | 返回的内存信息数组长度 |
+| **返回** | **描述** |
+|struct dtb_memreserve *| 内存预留信息数组的内存地址 |
+|RT_NULL | 该设备树没有预留内存信息 |
+
+## 读取节点的状态
+```c
+rt_bool_t fdt_get_dtb_node_status(struct dtb_node *dtb_node)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|dtb_node | 设备节点树节点 |
+| **返回** | **描述** |
+|RT_FALSE | 状态为禁用 |
+|RT_TRUE | 状态为使用 |
+
+## 用于参数为字符串列表的函数
+```c
+fdt_string_list(string, ...)
+```
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|string | 字符串集合 |
+
+## 读取节点compatible标志匹配
+```c
+rt_bool_t fdt_get_dtb_node_compatible_match(struct dtb_node *dtb_node, char **compatibles)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|dtb_node | 设备节点树节点 |
+|compatibles | 所有要匹配的字符串列表 |
+| **返回** | **描述** |
+|RT_FALSE | 匹配失败 |
+|RT_TRUE | 匹配成功 |
+
+## 读取属性值中的字符串
+```c
+char *fdt_get_dtb_string_list_value(void *value, int size, int index)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|value | 节点属性的内存地址 |
+|size | 节点属性的尺寸 |
+|index | 要读取字符串的索引 |
+| **返回** | **描述** |
+|char * | 字符串的内存地址 |
+|RT_NULL | 该索引没有字符串 |
+
+## 读取值为字符串属性下一个字符串
+```c
+char *fdt_get_dtb_string_list_value_next(void *value, void *end)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|value | 节点属性的内存地址 |
+|end | 节点属性的结束地址 |
+| **返回** | **描述** |
+|char * | 字符串的内存地址 |
+|RT_NULL | 没有下一个字符串 |
+
+## 读取值为u32属性的值
+```c
+rt_uint32_t fdt_get_dtb_cell_value(void *value)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|value | 节点属性的内存地址 |
+| **返回** | **描述** |
+|rt_uint32_t | 该值小端的值 |
+
+## 读取值为u8属性的值
+```c
+rt_uint8_t fdt_get_dtb_byte_value(void *value)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|value | 节点属性的内存地址 |
+| **返回** | **描述** |
+|rt_uint8_t | 该值小端的值 |
+
+# fdt foreach API
+
+## 遍历属性中字符串宏
+```c
+for_each_property_string(node_ptr, property_name, str, size)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|node_ptr | 设备节点树节点 |
+|property_name | 节点属性名称 |
+|str | 每次遍历到的字符串 |
+|size | 用于保存节点属性的尺寸 |
+
+## 遍历属性中u32宏
+```c
+for_each_property_cell(node_ptr, property_name, value, list, size)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|node_ptr | 设备节点树节点 |
+|property_name | 节点属性名称 |
+|value | 每次遍历到的值 |
+|list | 用于保存节点属性的值内存地址 |
+|size | 用于保存节点属性的尺寸 |
+
+## 遍历属性中u8宏
+```c
+for_each_property_byte(node_ptr, property_name, value, list, size)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|node_ptr | 设备节点树节点 |
+|property_name | 节点属性名称 |
+|value | 每次遍历到的值 |
+|list | 用于保存节点属性的值内存地址 |
+|size | 用于保存节点属性的尺寸 |
+
+## 遍历子节点宏
+```c
+for_each_node_child(node_ptr)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|node_ptr | 设备节点树节点 |
+
+## 遍历兄弟节点宏
+```c
+for_each_node_sibling(node_ptr)
+```
+
+| 参数 | 描述 |
+|:------------------|:------------------------------------|
+|node_ptr | 设备节点树节点 |
diff --git a/components/drivers/fdt/docs/examples.md b/components/drivers/fdt/docs/examples.md
new file mode 100644
index 0000000000..aa3e655987
--- /dev/null
+++ b/components/drivers/fdt/docs/examples.md
@@ -0,0 +1,81 @@
+# fdt示例 #
+
+在`examples`文件夹中存放`bcm2711-rpi-4-b.dtb`和`vexpress-v2p-ca9.dtb`可供测试,如果系统可以从bootloader或其他方式获取到bsp本身的dtb,也可以通过修改示例程序进行测试
+
+## fdt_dump
+```bash
+fdt_dump vexpress-v2p-ca9.dtb
+```
+
+#### 示例结果 ####
+```bash
+/dts-v1/;
+
+/ {
+ model = "V2P-CA9";
+ arm,hbi = <0x191>;
+ arm,vexpress,site = <0xf>;
+ compatible = "arm,vexpress,v2p-ca9", "arm,vexpress";
+ interrupt-parent = <0x1>;
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+
+ chosen {
+ };
+
+ aliases {
+ serial0 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@9000";
+ serial1 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@a000";
+ serial2 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@b000";
+ serial3 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@c000";
+ i2c0 = "/smb@4000000/motherboard/iofpga@7,00000000/i2c@16000";
+ i2c1 = "/smb@4000000/motherboard/iofpga@7,00000000/i2c@2000";
+ };
+
+...... 省略
+
+ hsb@e0000000 {
+ compatible = "simple-bus";
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ ranges = <0x0 0xe0000000 0x20000000>;
+ #interrupt-cells = <0x1>;
+ interrupt-map-mask = <0x0 0x3>;
+ interrupt-map = <0x0 0x0 0x1 0x0 0x24 0x4 0x0 0x1 0x1 0x0 0x25 0x4 0x0 0x2 0x1 0x0 0x26 0x4 0x0 0x3 0x1 0x0 0x27 0x4>;
+ };
+};
+```
+
+## fdt_test
+```bash
+fdt_test
+```
+
+#### 示例结果 ####
+```bash
+name = uart@9000
+reg = <0x9000,0x1000>;
+compatible = "arm,pl011","arm,primecell";
+
+name = cpus
+path = /cpus/cpu@0/
+path = /cpus/cpu@1/
+path = /cpus/cpu@2/
+path = /cpus/cpu@3/
+
+name = user1, lable = v2m:green:user1
+name = user2, lable = v2m:green:user2
+name = user3, lable = v2m:green:user3
+name = user4, lable = v2m:green:user4
+name = user5, lable = v2m:green:user5
+name = user6, lable = v2m:green:user6
+name = user7, lable = v2m:green:user7
+name = user8, lable = v2m:green:user8
+
+/memreserve/ 0x0000000000000000 0x0000000000001000;
+
+phandle = <0x9>
+name = bt_pins
+path = /soc/gpio@7e200000/bt_pins/
+brcm,pins = [2d 00]
+``` \ No newline at end of file
diff --git a/components/drivers/fdt/docs/version.md b/components/drivers/fdt/docs/version.md
new file mode 100644
index 0000000000..3c3499d91a
--- /dev/null
+++ b/components/drivers/fdt/docs/version.md
@@ -0,0 +1,7 @@
+# 版本和修订 #
+
+| Date | Version | Author | Note |
+| -------- | :-----: | :---- | :---- |
+| 2021-9-1 | v0.0.1 | GuEe-GUI | 初始版本 |
+| 2021-11-11 | v1.0.0 | GuEe-GUI | API确定版本 |
+| | | | | \ No newline at end of file
diff --git a/components/drivers/fdt/examples/SConscript b/components/drivers/fdt/examples/SConscript
new file mode 100644
index 0000000000..30867a1de3
--- /dev/null
+++ b/components/drivers/fdt/examples/SConscript
@@ -0,0 +1,10 @@
+
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+CPPPATH = [cwd]
+
+group = DefineGroup('FDT', src, depend = ['FDT_USING_DEBUG'], CPPPATH = CPPPATH)
+
+Return('group') \ No newline at end of file
diff --git a/components/drivers/fdt/examples/fdt_test.c b/components/drivers/fdt/examples/fdt_test.c
new file mode 100644
index 0000000000..4bf2879256
--- /dev/null
+++ b/components/drivers/fdt/examples/fdt_test.c
@@ -0,0 +1,143 @@
+#include <rtthread.h>
+#include "dtb_node.h"
+
+int dtb_test()
+{
+ void *fdt;
+
+ if ((fdt = dtb_node_load_from_fs("vexpress-v2p-ca9.dtb")) != RT_NULL)
+ {
+ struct dtb_node *dtb_node_list = dtb_node_get_dtb_list(fdt);
+ if (dtb_node_list != RT_NULL)
+ {
+ struct dtb_node *serial0 = dtb_node_get_dtb_node_by_path(dtb_node_list, "/smb@4000000/motherboard/iofpga@7,00000000/uart@9000");
+ struct dtb_node *cpu = dtb_node_get_dtb_node_by_path(dtb_node_list, "/cpus");
+ struct dtb_node *user1 = dtb_node_get_dtb_node_by_path(dtb_node_list, "/smb@4000000/motherboard/leds/user1");
+
+ if (serial0 != RT_NULL)
+ {
+ int property_size;
+ rt_uint32_t u32_value;
+ rt_uint32_t *u32_ptr;
+ char *str_ptr;
+
+ rt_kprintf("name = %s\n", serial0->name);
+
+ rt_kputs("reg = <");
+ for_each_property_cell(serial0, "reg", u32_value, u32_ptr, property_size)
+ {
+ rt_kprintf("0x%x,", u32_value);
+ }
+ rt_kputs("\b>;\n");
+
+ rt_kputs("compatible = ");
+ for_each_property_string(serial0, "compatible", str_ptr, property_size)
+ {
+ rt_kprintf("\"%s\",", str_ptr);
+ }
+ rt_kputs("\b;\n");
+ }
+
+ if (cpu != RT_NULL)
+ {
+ struct dtb_node *cpu_child = cpu;
+
+ rt_kprintf("\nname = %s\n", cpu->name);
+
+ for_each_node_child(cpu_child)
+ {
+ rt_kprintf("path = %s\n", cpu_child->path);
+ }
+ }
+
+ if (user1 != RT_NULL)
+ {
+ struct dtb_node *user = user1;
+
+ rt_kprintf("\nname = %s, lable = %s\n", user1->name, dtb_node_get_dtb_node_property(user1, "label", RT_NULL));
+
+ for_each_node_sibling(user)
+ {
+ rt_kprintf("name = %s, lable = %s\n", user->name, dtb_node_get_dtb_node_property(user, "label", RT_NULL));
+ }
+ }
+ }
+ dtb_node_free_dtb_list(dtb_node_list);
+ }
+
+ if ((fdt = dtb_node_load_from_fs("bcm2711-rpi-4-b.dtb")) != RT_NULL)
+ {
+ struct dtb_node *dtb_node_list = dtb_node_get_dtb_list(fdt);
+ if (dtb_node_list != RT_NULL)
+ {
+ struct dtb_node *bt_pins;
+ int memreserve_size;
+ struct dtb_memreserve *memreserve;
+
+ bt_pins = dtb_node_get_dtb_node_by_name_DFS(dtb_node_list, "bt_pins");
+ bt_pins = dtb_node_get_dtb_node_by_name_BFS(dtb_node_list, bt_pins->name);
+ bt_pins = dtb_node_get_dtb_node_by_phandle_DFS(dtb_node_list, bt_pins->handle);
+ bt_pins = dtb_node_get_dtb_node_by_phandle_BFS(dtb_node_list, bt_pins->handle);
+
+ memreserve = dtb_node_get_dtb_memreserve(bt_pins, &memreserve_size);
+ if (memreserve_size > 0)
+ {
+ int i;
+ for (i = 0; i < memreserve_size; ++i)
+ {
+ rt_kputs("\n/memreserve/\t");
+ if (IN_64BITS_MODE)
+ {
+ rt_kprintf("0x%016x 0x%016x;", memreserve[i].address, memreserve[i].size);
+ }
+ else
+ {
+ rt_kprintf("0x%08x%08x 0x%08x%08x;", memreserve[i].address, memreserve[i].size);
+ }
+ }
+ }
+
+ if (bt_pins != RT_NULL)
+ {
+ int property_size;
+ rt_uint8_t u8_value;
+ rt_uint8_t *u8_ptr;
+
+ rt_kprintf("\n\nphandle = <0x%x>\n", bt_pins->handle);
+ rt_kprintf("name = %s\n", bt_pins->name);
+ rt_kprintf("path = %s\n", bt_pins->path);
+
+ rt_kputs("brcm,pins = [");
+ for_each_property_byte(bt_pins, "brcm,pins", u8_value, u8_ptr, property_size)
+ {
+ rt_kprintf("%02x ", u8_value);
+ }
+ rt_kputs("\b]\n");
+ }
+ }
+ dtb_node_free_dtb_list(dtb_node_list);
+ }
+ return 0;
+}
+MSH_CMD_EXPORT(dtb_test, dtb API test);
+
+int dtb_dump(int argc, char** argv)
+{
+ struct dtb_node *dtb_root_node = get_dtb_node_head();
+ if(argc == 1)
+ {
+ if (dtb_root_node != RT_NULL)
+ {
+ dtb_node_get_dts_dump(dtb_root_node);
+ }
+ }
+ else
+ {
+ rt_kprintf("invailed parameter\n");
+ }
+
+ return 0;
+}
+MSH_CMD_EXPORT(dtb_dump, dtb dump from mem);
+
+
diff --git a/components/drivers/fdt/inc/dtb_node.h b/components/drivers/fdt/inc/dtb_node.h
new file mode 100644
index 0000000000..be304a48c8
--- /dev/null
+++ b/components/drivers/fdt/inc/dtb_node.h
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef _DTB_NODE_H__
+#define _DTB_NODE_H__
+
+#include "libfdt_env.h"
+#include <rtthread.h>
+
+//#define RT_DTB_DEBUG
+#ifdef RT_DTB_DEBUG
+#define debug(fmt, args...) rt_kprintf(fmt, ##args)
+#else
+#define debug(fmt, args...)
+#endif
+
+#define ERR_PTR(err) ((void *)((long)(err)))
+#define PTR_ERR(ptr) ((long)(ptr))
+#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000))
+
+#define DEV_ROOT_NODE_ADDR_CELLS_DEFAULT 2
+#define DEV_ROOT_NODE_SIZE_CELLS_DEFAULT 1
+/* will be optimized to only u64 or u32 by gcc */
+#define IN_64BITS_MODE (sizeof(void *) == 8)
+
+#define FDT_ROOT_ADDR_CELLS_DEFAULT 1
+#define FDT_ROOT_SIZE_CELLS_DEFAULT 1
+
+#define FDT_DTB_ALL_NODES_PATH_SIZE (32 * 1024)
+#define FDT_DTB_PAD_SIZE 1024
+
+#define FDT_RET_NO_MEMORY 2
+#define FDT_RET_NO_LOADED 1
+#define FDT_RET_GET_OK 0
+#define FDT_RET_GET_EMPTY (-1)
+
+typedef uint32_t phandle;
+
+struct dtb_memreserve
+{
+ uintptr_t address;
+ size_t size;
+};
+
+struct dtb_header
+{
+ char root, zero; /* "/" */
+ struct dtb_memreserve *memreserve;
+ size_t memreserve_sz;
+};
+
+struct dtb_property
+{
+ const char *name;
+ int size;
+ void *value;
+
+ struct dtb_property *next;
+};
+
+struct dtb_node
+{
+ union
+ {
+ const char *name;
+ const struct dtb_header *header;
+ };
+ const char *path;
+ phandle handle;
+
+ struct dtb_property *properties;
+ struct dtb_node *parent;
+ struct dtb_node *child;
+ struct dtb_node *sibling;
+};
+
+#define FDT_MAX_PHANDLE_ARGS 16
+
+/**
+ * struct dtb_node_phandle_args - structure to hold phandle and arguments
+ *
+ * This is used when decoding a phandle in a device tree property. Typically
+ * these look like this:
+ *
+ * wibble {
+ * phandle = <5>;
+ * };
+ *
+ * ...
+ * some-prop = <&wibble 1 2 3>
+ *
+ * Here &node is the phandle of the node 'wibble', i.e. 5. There are three
+ * arguments: 1, 2, 3.
+ *
+ * So when decoding the phandle in some-prop, np will point to wibble,
+ * args_count will be 3 and the three arguments will be in args.
+ *
+ * @np: Node that the phandle refers to
+ * @args_count: Number of arguments
+ * @args: Argument values
+ */
+struct fdt_phandle_args
+{
+ struct dtb_node *np;
+ int args_count;
+ uint32_t args[FDT_MAX_PHANDLE_ARGS];
+};
+
+/*
+ * A single signal can be specified via a range of minimal and maximal values
+ * with a typical value, that lies somewhere inbetween.
+ */
+struct timing_entry
+{
+ uint32_t min;
+ uint32_t typ;
+ uint32_t max;
+};
+
+void *get_fdt_blob(void);
+struct dtb_node *get_dtb_node_head(void);
+rt_bool_t dtb_node_active(void);
+int device_tree_setup(void *mem_addr);
+
+void *dtb_node_load_from_fs(char *dtb_filename);
+void *dtb_node_load_from_memory(void *dtb_ptr, rt_bool_t is_clone);
+
+size_t dtb_node_set_linux_cmdline(void *fdt, char *cmdline);
+size_t dtb_node_set_linux_initrd(void *fdt, uint64_t initrd_addr, size_t initrd_size);
+
+size_t dtb_node_set_dtb_property(void *fdt, char *pathname, char *property_name, uint32_t *cells, size_t cells_size);
+size_t dtb_node_add_dtb_memreserve(void *fdt, uint64_t address, uint64_t size);
+size_t dtb_node_del_dtb_memreserve(void *fdt, uint64_t address);
+
+int dtb_node_get_exec_status();
+struct dtb_node *dtb_node_get_dtb_list(void *fdt);
+void dtb_node_free_dtb_list(struct dtb_node *dtb_node_head);
+void dtb_node_get_dts_dump(struct dtb_node *dtb_node_head);
+void dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node_head, void(callback(struct dtb_node *dtb_node)));
+
+struct dtb_node *dtb_node_get_dtb_node_by_name_DFS(struct dtb_node *dtb_node, const char *nodename);
+struct dtb_node *dtb_node_get_dtb_node_by_name_BFS(struct dtb_node *dtb_node, const char *nodename);
+struct dtb_node *dtb_node_get_dtb_node_by_path(struct dtb_node *dtb_node, const char *pathname);
+struct dtb_node *dtb_node_get_dtb_node_by_phandle_DFS(struct dtb_node *dtb_node, phandle handle);
+struct dtb_node *dtb_node_get_dtb_node_by_phandle_BFS(struct dtb_node *dtb_node, phandle handle);
+void dtb_node_get_dtb_node_cells(struct dtb_node *dtb_node, int *addr_cells, int *size_cells);
+struct dtb_memreserve *dtb_node_get_dtb_memreserve(struct dtb_node *dtb_node, int *memreserve_size);
+uint8_t dtb_node_get_dtb_byte_value(void *value);
+
+char *dtb_node_get_dtb_string_list_value(void *value, int size, int index);
+char *dtb_node_get_dtb_string_list_value_next(void *value, void *end);
+uint32_t dtb_node_get_dtb_cell_value(void *value);
+
+rt_bool_t dtb_node_get_dtb_node_status(const struct dtb_node *dtb_node);
+rt_bool_t dtb_node_get_dtb_node_compatible_match(const struct dtb_node *dtb_node, const char *compatibles);
+
+/*dtb_node_access.c */
+int dtb_node_read_u32(const struct dtb_node *dn, const char *propname, uint32_t *outp);
+uint32_t dtb_node_read_u32_default(const struct dtb_node *node, const char *propname, uint32_t def);
+int dtb_node_read_u32_index(const struct dtb_node *node, const char *propname, int index,
+ uint32_t *outp);
+uint32_t dtb_node_read_u32_index_default(const struct dtb_node *node, const char *propname, int index,
+ uint32_t def);
+int dtb_node_read_u32_array(const struct dtb_node *dn, const char *propname,
+ uint32_t *out_values, size_t sz);
+int dtb_node_read_u32_index(const struct dtb_node *dn, const char *propname,
+ int index, uint32_t *outp);
+int dtb_node_read_s32_default(const struct dtb_node *node, const char *propname, int32_t def);
+int dtb_node_read_u64(const struct dtb_node *dn, const char *propname, uint64_t *outp);
+uint64_t dtb_node_read_u64_default(const struct dtb_node *node, const char *propname, uint64_t def);
+
+int dtb_node_n_addr_cells(const struct dtb_node *dn);
+int dtb_node_n_size_cells(const struct dtb_node *dn);
+int dtb_node_simple_addr_cells(const struct dtb_node *np);
+int dtb_node_simple_size_cells(const struct dtb_node *np);
+
+struct dtb_node *dtb_node_find_all_nodes(const struct dtb_node *prev);
+struct dtb_node *dtb_node_find_node_by_phandle(phandle handle);
+struct dtb_node *dtb_node_find_compatible_node(struct dtb_node *from, const char *compatible);
+void *dtb_node_get_dtb_node_property_value(const struct dtb_node *dtb_node, const char *property_name, int *property_size);
+struct dtb_property *dtb_node_get_dtb_node_property(const struct dtb_node *dtb_node, const char *property_name, int *property_size);
+const struct dtb_node *dtb_node_find_node_by_prop_value(struct dtb_node *from, const char *propname, const void *propval, int proplen);
+struct dtb_node *dtb_node_find_node_opts_by_path(const char *path,
+ const char **opts);
+
+static inline struct dtb_node *dtb_node_find_node_by_path(const char *path)
+{
+ return dtb_node_find_node_opts_by_path(path, NULL);
+}
+
+rt_bool_t dtb_node_device_is_available(const struct dtb_node *device);
+struct dtb_node *dtb_node_get_parent(const struct dtb_node *node);
+int dtb_node_property_match_string(const struct dtb_node *dn, const char *propname,
+ const char *string);
+int dtb_node_property_read_string_helper(const struct dtb_node *dn,
+ const char *propname, const char **out_strs,
+ size_t sz, int skip);
+/**
+ * of_property_read_string_index() - Find and read a string from a multiple
+ * strings property.
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @index: index of the string in the list of strings
+ * @out_string: pointer to null terminated return string, modified only if
+ * return value is 0.
+ *
+ * Search for a property in a device tree node and retrieve a null
+ * terminated string value (pointer to data, not a copy) in the list of strings
+ * contained in that property.
+ * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
+ * property does not have a value, and -EILSEQ if the string is not
+ * null-terminated within the length of the property data.
+ *
+ * The out_string pointer is modified only if a valid string can be decoded.
+ */
+static inline int dtb_node_property_read_string_index(const struct dtb_node *dn,
+ const char *propname,
+ int index, const char **output)
+{
+ int rc = dtb_node_property_read_string_helper(dn, propname, output, 1, index);
+ return rc < 0 ? rc : 0;
+}
+
+/**
+ * of_property_count_strings() - Find and return the number of strings from a
+ * multiple strings property.
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ *
+ * Search for a property in a device tree node and retrieve the number of null
+ * terminated string contain in it. Returns the number of strings on
+ * success, -EINVAL if the property does not exist, -ENODATA if property
+ * does not have a value, and -EILSEQ if the string is not null-terminated
+ * within the length of the property data.
+ */
+static inline int dtb_node_property_count_strings(const struct dtb_node *dn,
+ const char *propname)
+{
+ return dtb_node_property_read_string_helper(dn, propname, NULL, 0, 0);
+}
+
+struct dtb_node *dtb_node_parse_phandle(const struct dtb_node *dn,
+ const char *phandle_name, int index);
+int dtb_node_parse_phandle_with_args(const struct dtb_node *dn,
+ const char *list_name, const char *cells_name,
+ int index, struct fdt_phandle_args *out_args);
+int dtb_node_count_phandle_with_args(const struct dtb_node *dn,
+ const char *list_name, const char *cells_name);
+
+/* dtb_node_addr.c */
+const uint32_t *dtb_node_get_address(const struct dtb_node *dev, int index,
+ uint64_t *size, unsigned int *flags);
+
+#define dtb_node_string_list(string, ...) ((char *[]){string, ##__VA_ARGS__, NULL})
+
+#define for_each_property_string(node_ptr, property_name, str, size) \
+ for (str = dtb_node_get_dtb_node_property_value(node_ptr, property_name, &size), \
+ size += (typeof(size))(size_t)str; \
+ str && *str; \
+ str = dtb_node_get_dtb_string_list_value_next((void *)str, (void *)(size_t)size))
+
+#define for_each_property_cell(node_ptr, property_name, value, list, size) \
+ for (list = dtb_node_get_dtb_node_property_value(node_ptr, property_name, &size), \
+ value = dtb_node_get_dtb_cell_value(list), \
+ size /= sizeof(uint32_t); \
+ size > 0; \
+ value = dtb_node_get_dtb_cell_value(++list), --size)
+
+#define for_each_property_byte(node_ptr, property_name, value, list, size) \
+ for (list = dtb_node_get_dtb_node_property_value(node_ptr, property_name, &size), \
+ value = dtb_node_get_dtb_byte_value(list); \
+ size > 0; \
+ value = dtb_node_get_dtb_byte_value(++list), --size)
+
+#define for_each_node_child(node_ptr) \
+ for (node_ptr = (node_ptr ? node_ptr->child : NULL); \
+ node_ptr != NULL; \
+ node_ptr = node_ptr->sibling)
+
+#define for_each_node_sibling(node_ptr) \
+ for (node_ptr = (node_ptr ? node_ptr->sibling : NULL); \
+ node_ptr != NULL; \
+ node_ptr = node_ptr->sibling)
+
+#define for_each_of_allnodes_from(from, dn) \
+ for (dn = dtb_node_find_all_nodes(from); dn; dn = dtb_node_find_all_nodes(dn))
+
+#define for_each_of_allnodes(dn) for_each_of_allnodes_from(NULL, dn)
+
+#define dtb_node_get(x) (x)
+static inline void dtb_node_put(const struct dtb_node *np)
+{
+}
+
+/* Helper to read a big number; size is in cells (not bytes) */
+static inline uint64_t dtb_node_read_number(const uint32_t *cell, int size)
+{
+ uint64_t r = 0;
+ while (size--)
+ r = (r << 32) | fdt32_to_cpu(*(cell++));
+ return r;
+}
+
+/**
+ * ofnode_valid() - check if an ofnode is valid
+ *
+ * @return true if the reference contains a valid ofnode, RT_FALSE if it is NULL
+ */
+static inline rt_bool_t dtb_node_valid(const struct dtb_node *node)
+{
+ if (dtb_node_active())
+ return node != NULL;
+ return RT_FALSE;
+}
+
+/*dtb_base.c */
+rt_bool_t dtb_node_read_bool(const struct dtb_node *node, const char *propname);
+const void *dtb_node_read_prop(const struct dtb_node *node, const char *propname, int *sizep);
+const char *dtb_node_read_string(const struct dtb_node *node, const char *propname);
+const struct dtb_node *dtb_node_find_subnode(const struct dtb_node *node, const char *subnode_name);
+int dtb_node_read_u32_array(const struct dtb_node *node, const char *propname,
+ uint32_t *out_values, size_t sz);
+struct dtb_node *dtb_node_first_subnode(const struct dtb_node *node);
+struct dtb_node *dtb_node_next_subnode(const struct dtb_node *node);
+struct dtb_node *dtb_node_get_parent(const struct dtb_node *node);
+const char *dtb_node_get_name(const struct dtb_node *node);
+struct dtb_node *dtb_node_get_by_phandle(uint phandle);
+int dtb_node_read_size(const struct dtb_node *node, const char *propname);
+size_t dtb_node_get_addr_index(const struct dtb_node *node, int index);
+size_t dtb_node_get_addr(const struct dtb_node *node);
+int dtb_node_stringlist_search(const struct dtb_node *node, const char *property,
+ const char *string);
+int dtb_node_read_string_index(const struct dtb_node *node, const char *property, int index,
+ const char **outp);
+int dtb_node_read_string_count(const struct dtb_node *node, const char *property);
+struct dtb_node *dtb_node_path(const char *path);
+const char *dtb_node_get_chosen_prop(const char *name);
+struct dtb_node *dtb_node_get_chosen_node(const char *name);
+const void *dtb_node_get_property(const struct dtb_node *node, const char *propname, int *lenp);
+rt_bool_t dtb_node_is_available(const struct dtb_node *node);
+size_t dtb_node_get_addr_size(const struct dtb_node *node, const char *property,
+ size_t *sizep);
+const uint8_t *dtb_node_read_u8_array_ptr(const struct dtb_node *node, const char *propname, size_t sz);
+int dtb_node_find_all_compatible_node(const struct dtb_node *from, const char *compatible, struct dtb_node **node_table, int max_num, int *node_num);
+int dtb_node_write_prop(const struct dtb_node *node, const char *propname, int len,
+ const void *value);
+int dtb_node_write_string(const struct dtb_node *node, const char *propname, const char *value);
+int dtb_node_set_enabled(const struct dtb_node *node, rt_bool_t value);
+int dtb_node_irq_get(struct dtb_node *dev, int index);
+int dtb_node_irq_get_byname(struct dtb_node *dev, const char *name);
+int dtb_node_irq_count(struct dtb_node *dev);
+
+/**
+ * dtb_node_for_each_subnode() - iterate over all subnodes of a parent
+ *
+ * @node: child node (ofnode, lvalue)
+ * @parent: parent node (ofnode)
+ *
+ * This is a wrapper around a for loop and is used like so:
+ *
+ * ofnode node;
+ *
+ * dtb_node_for_each_subnode(node, parent) {
+ * Use node
+ * ...
+ * }
+ *
+ * Note that this is implemented as a macro and @node is used as
+ * iterator in the loop. The parent variable can be a constant or even a
+ * literal.
+ */
+#define dtb_node_for_each_subnode(node, parent) \
+ for (node = dtb_node_first_subnode(parent); \
+ dtb_node_valid(node); \
+ node = dtb_node_next_subnode(node))
+
+#endif /* RT_FDT_H__ */
diff --git a/components/drivers/fdt/libfdt/SConscript b/components/drivers/fdt/libfdt/SConscript
new file mode 100644
index 0000000000..638a457bfe
--- /dev/null
+++ b/components/drivers/fdt/libfdt/SConscript
@@ -0,0 +1,10 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+
+CPPPATH = [cwd]
+
+group = DefineGroup('FDT', src, depend = ['RT_USING_FDTLIB'], CPPPATH = CPPPATH)
+
+Return('group') \ No newline at end of file
diff --git a/components/drivers/fdt/libfdt/fdt.c b/components/drivers/fdt/libfdt/fdt.c
new file mode 100644
index 0000000000..1d4c606985
--- /dev/null
+++ b/components/drivers/fdt/libfdt/fdt.c
@@ -0,0 +1,249 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+#include "fdt.h"
+#include "libfdt.h"
+#include "libfdt_internal.h"
+
+int fdt_check_header(const void *fdt)
+{
+ if (fdt_magic(fdt) == FDT_MAGIC) {
+ /* Complete tree */
+ if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
+ return -FDT_ERR_BADVERSION;
+ if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
+ return -FDT_ERR_BADVERSION;
+ } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
+ /* Unfinished sequential-write blob */
+ if (fdt_size_dt_struct(fdt) == 0)
+ return -FDT_ERR_BADSTATE;
+ } else {
+ return -FDT_ERR_BADMAGIC;
+ }
+
+ return 0;
+}
+
+const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
+{
+ unsigned absoffset = offset + fdt_off_dt_struct(fdt);
+
+ if ((absoffset < offset)
+ || ((absoffset + len) < absoffset)
+ || (absoffset + len) > fdt_totalsize(fdt))
+ return NULL;
+
+ if (fdt_version(fdt) >= 0x11)
+ if (((offset + len) < offset)
+ || ((offset + len) > fdt_size_dt_struct(fdt)))
+ return NULL;
+
+ return _fdt_offset_ptr(fdt, offset);
+}
+
+uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
+{
+ const fdt32_t *tagp, *lenp;
+ uint32_t tag;
+ int offset = startoffset;
+ const char *p;
+
+ *nextoffset = -FDT_ERR_TRUNCATED;
+ tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
+ if (!tagp)
+ return FDT_END; /* premature end */
+ tag = fdt32_to_cpu(*tagp);
+ offset += FDT_TAGSIZE;
+
+ *nextoffset = -FDT_ERR_BADSTRUCTURE;
+ switch (tag) {
+ case FDT_BEGIN_NODE:
+ /* skip name */
+ do {
+ p = fdt_offset_ptr(fdt, offset++, 1);
+ } while (p && (*p != '\0'));
+ if (!p)
+ return FDT_END; /* premature end */
+ break;
+
+ case FDT_PROP:
+ lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
+ if (!lenp)
+ return FDT_END; /* premature end */
+ /* skip-name offset, length and value */
+ offset += sizeof(struct fdt_property) - FDT_TAGSIZE
+ + fdt32_to_cpu(*lenp);
+ break;
+
+ case FDT_END:
+ case FDT_END_NODE:
+ case FDT_NOP:
+ break;
+
+ default:
+ return FDT_END;
+ }
+
+ if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
+ return FDT_END; /* premature end */
+
+ *nextoffset = FDT_TAGALIGN(offset);
+ return tag;
+}
+
+int _fdt_check_node_offset(const void *fdt, int offset)
+{
+ if ((offset < 0) || (offset % FDT_TAGSIZE)
+ || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
+ return -FDT_ERR_BADOFFSET;
+
+ return offset;
+}
+
+int _fdt_check_prop_offset(const void *fdt, int offset)
+{
+ if ((offset < 0) || (offset % FDT_TAGSIZE)
+ || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
+ return -FDT_ERR_BADOFFSET;
+
+ return offset;
+}
+
+int fdt_next_node(const void *fdt, int offset, int *depth)
+{
+ int nextoffset = 0;
+ uint32_t tag;
+
+ if (offset >= 0)
+ if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0)
+ return nextoffset;
+
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_PROP:
+ case FDT_NOP:
+ break;
+
+ case FDT_BEGIN_NODE:
+ if (depth)
+ (*depth)++;
+ break;
+
+ case FDT_END_NODE:
+ if (depth && ((--(*depth)) < 0))
+ return nextoffset;
+ break;
+
+ case FDT_END:
+ if ((nextoffset >= 0)
+ || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
+ return -FDT_ERR_NOTFOUND;
+ else
+ return nextoffset;
+ }
+ } while (tag != FDT_BEGIN_NODE);
+
+ return offset;
+}
+
+int fdt_first_subnode(const void *fdt, int offset)
+{
+ int depth = 0;
+
+ offset = fdt_next_node(fdt, offset, &depth);
+ if (offset < 0 || depth != 1)
+ return -FDT_ERR_NOTFOUND;
+
+ return offset;
+}
+
+int fdt_next_subnode(const void *fdt, int offset)
+{
+ int depth = 1;
+
+ /*
+ * With respect to the parent, the depth of the next subnode will be
+ * the same as the last.
+ */
+ do {
+ offset = fdt_next_node(fdt, offset, &depth);
+ if (offset < 0 || depth < 1)
+ return -FDT_ERR_NOTFOUND;
+ } while (depth > 1);
+
+ return offset;
+}
+
+const char *_fdt_find_string(const char *strtab, int tabsize, const char *s)
+{
+ int len = strlen(s) + 1;
+ const char *last = strtab + tabsize - len;
+ const char *p;
+
+ for (p = strtab; p <= last; p++)
+ if (memcmp(p, s, len) == 0)
+ return p;
+ return NULL;
+}
+
+int fdt_move(const void *fdt, void *buf, int bufsize)
+{
+ FDT_CHECK_HEADER(fdt);
+
+ if (fdt_totalsize(fdt) > bufsize)
+ return -FDT_ERR_NOSPACE;
+
+ memmove(buf, fdt, fdt_totalsize(fdt));
+ return 0;
+}
diff --git a/components/drivers/fdt/libfdt/fdt.h b/components/drivers/fdt/libfdt/fdt.h
new file mode 100644
index 0000000000..526aedb515
--- /dev/null
+++ b/components/drivers/fdt/libfdt/fdt.h
@@ -0,0 +1,111 @@
+#ifndef _FDT_H
+#define _FDT_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * Copyright 2012 Kim Phillips, Freescale Semiconductor.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __ASSEMBLY__
+
+struct fdt_header {
+ fdt32_t magic; /* magic word FDT_MAGIC */
+ fdt32_t totalsize; /* total size of DT block */
+ fdt32_t off_dt_struct; /* offset to structure */
+ fdt32_t off_dt_strings; /* offset to strings */
+ fdt32_t off_mem_rsvmap; /* offset to memory reserve map */
+ fdt32_t version; /* format version */
+ fdt32_t last_comp_version; /* last compatible version */
+
+ /* version 2 fields below */
+ fdt32_t boot_cpuid_phys; /* Which physical CPU id we're
+ booting on */
+ /* version 3 fields below */
+ fdt32_t size_dt_strings; /* size of the strings block */
+
+ /* version 17 fields below */
+ fdt32_t size_dt_struct; /* size of the structure block */
+};
+
+struct fdt_reserve_entry {
+ fdt64_t address;
+ fdt64_t size;
+};
+
+struct fdt_node_header {
+ fdt32_t tag;
+ char name[0];
+};
+
+struct fdt_property {
+ fdt32_t tag;
+ fdt32_t len;
+ fdt32_t nameoff;
+ char data[0];
+};
+
+#endif /* !__ASSEMBLY */
+
+#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */
+#define FDT_TAGSIZE sizeof(fdt32_t)
+
+#define FDT_BEGIN_NODE 0x1 /* Start node: full name */
+#define FDT_END_NODE 0x2 /* End node */
+#define FDT_PROP 0x3 /* Property: name off,
+ size, content */
+#define FDT_NOP 0x4 /* nop */
+#define FDT_END 0x9
+
+#define FDT_V1_SIZE (7*sizeof(fdt32_t))
+#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t))
+#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t))
+#define FDT_V16_SIZE FDT_V3_SIZE
+#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t))
+
+#endif /* _FDT_H */
diff --git a/components/drivers/fdt/libfdt/fdt_addresses.c b/components/drivers/fdt/libfdt/fdt_addresses.c
new file mode 100644
index 0000000000..a8f2ad5ebc
--- /dev/null
+++ b/components/drivers/fdt/libfdt/fdt_addresses.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause)
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2014 David Gibson <[email protected]>
+ * Copyright (C) 2018 embedded brains GmbH
+ */
+#include "libfdt_env.h"
+#include <fdt.h>
+#include <libfdt.h>
+#include "libfdt_internal.h"
+
+static int fdt_cells(const void *fdt, int nodeoffset, const char *name)
+{
+ const fdt32_t *c;
+ uint32_t val;
+ int len;
+
+ c = fdt_getprop(fdt, nodeoffset, name, &len);
+ if (!c)
+ return len;
+
+ if (len != sizeof(*c))
+ return -FDT_ERR_BADNCELLS;
+
+ val = fdt32_to_cpu(*c);
+ if (val > FDT_MAX_NCELLS)
+ return -FDT_ERR_BADNCELLS;
+
+ return (int)val;
+}
+
+int fdt_address_cells(const void *fdt, int nodeoffset)
+{
+ int val;
+
+ val = fdt_cells(fdt, nodeoffset, "#address-cells");
+ if (val == 0)
+ return -FDT_ERR_BADNCELLS;
+ if (val == -FDT_ERR_NOTFOUND)
+ return 2;
+ return val;
+}
+
+int fdt_size_cells(const void *fdt, int nodeoffset)
+{
+ int val;
+
+ val = fdt_cells(fdt, nodeoffset, "#size-cells");
+ if (val == -FDT_ERR_NOTFOUND)
+ return 1;
+ return val;
+}
+
+/* This function assumes that [address|size]_cells is 1 or 2 */
+int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset,
+ const char *name, uint64_t addr, uint64_t size)
+{
+ int addr_cells, size_cells, ret;
+ uint8_t data[sizeof(fdt64_t) * 2], *prop;
+
+ ret = fdt_address_cells(fdt, parent);
+ if (ret < 0)
+ return ret;
+ addr_cells = ret;
+
+ ret = fdt_size_cells(fdt, parent);
+ if (ret < 0)
+ return ret;
+ size_cells = ret;
+
+ /* check validity of address */
+ prop = data;
+ if (addr_cells == 1) {
+ if ((addr > UINT32_MAX) || ((UINT32_MAX + 1 - addr) < size))
+ return -FDT_ERR_BADVALUE;
+
+ fdt32_st(prop, (uint32_t)addr);
+ } else if (addr_cells == 2) {
+ fdt64_st(prop, addr);
+ } else {
+ return -FDT_ERR_BADNCELLS;
+ }
+
+ /* check validity of size */
+ prop += addr_cells * sizeof(fdt32_t);
+ if (size_cells == 1) {
+ if (size > UINT32_MAX)
+ return -FDT_ERR_BADVALUE;
+
+ fdt32_st(prop, (uint32_t)size);
+ } else if (size_cells == 2) {
+ fdt64_st(prop, size);
+ } else {
+ return -FDT_ERR_BADNCELLS;
+ }
+
+ return fdt_appendprop(fdt, nodeoffset, name, data,
+ (addr_cells + size_cells) * sizeof(fdt32_t));
+}
diff --git a/components/drivers/fdt/libfdt/fdt_empty_tree.c b/components/drivers/fdt/libfdt/fdt_empty_tree.c
new file mode 100644
index 0000000000..26ab2d3641
--- /dev/null
+++ b/components/drivers/fdt/libfdt/fdt_empty_tree.c
@@ -0,0 +1,84 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2012 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+
+#include "fdt.h"
+#include "libfdt.h"
+
+#include "libfdt_internal.h"
+
+int fdt_create_empty_tree(void *buf, int bufsize)
+{
+ int err;
+
+ err = fdt_create(buf, bufsize);
+ if (err)
+ return err;
+
+ err = fdt_finish_reservemap(buf);
+ if (err)
+ return err;
+
+ err = fdt_begin_node(buf, "");
+ if (err)
+ return err;
+
+ err = fdt_end_node(buf);
+ if (err)
+ return err;
+
+ err = fdt_finish(buf);
+ if (err)
+ return err;
+
+ return fdt_open_into(buf, buf, bufsize);
+}
+
diff --git a/components/drivers/fdt/libfdt/fdt_ro.c b/components/drivers/fdt/libfdt/fdt_ro.c
new file mode 100644
index 0000000000..984e8fe227
--- /dev/null
+++ b/components/drivers/fdt/libfdt/fdt_ro.c
@@ -0,0 +1,701 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+#include "fdt.h"
+#include "libfdt.h"
+#include "libfdt_internal.h"
+
+static int _fdt_nodename_eq(const void *fdt, int offset,
+ const char *s, int len)
+{
+ const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1);
+
+ if (! p)
+ /* short match */
+ return 0;
+
+ if (memcmp(p, s, len) != 0)
+ return 0;
+
+ if (p[len] == '\0')
+ return 1;
+ else if (!memchr(s, '@', len) && (p[len] == '@'))
+ return 1;
+ else
+ return 0;
+}
+
+const char *fdt_string(const void *fdt, int stroffset)
+{
+ return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
+}
+
+static int _fdt_string_eq(const void *fdt, int stroffset,
+ const char *s, int len)
+{
+ const char *p = fdt_string(fdt, stroffset);
+
+ return (strlen(p) == len) && (memcmp(p, s, len) == 0);
+}
+
+uint32_t fdt_get_max_phandle(const void *fdt)
+{
+ uint32_t max_phandle = 0;
+ int offset;
+
+ for (offset = fdt_next_node(fdt, -1, NULL);;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ uint32_t phandle;
+
+ if (offset == -FDT_ERR_NOTFOUND)
+ return max_phandle;
+
+ if (offset < 0)
+ return (uint32_t)-1;
+
+ phandle = fdt_get_phandle(fdt, offset);
+ if (phandle == (uint32_t)-1)
+ continue;
+
+ if (phandle > max_phandle)
+ max_phandle = phandle;
+ }
+
+ return 0;
+}
+
+int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size)
+{
+ FDT_CHECK_HEADER(fdt);
+ *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address);
+ *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size);
+ return 0;
+}
+
+int fdt_num_mem_rsv(const void *fdt)
+{
+ int i = 0;
+
+ while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0)
+ i++;
+ return i;
+}
+
+static int _nextprop(const void *fdt, int offset)
+{
+ uint32_t tag;
+ int nextoffset;
+
+ do {
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+
+ switch (tag) {
+ case FDT_END:
+ if (nextoffset >= 0)
+ return -FDT_ERR_BADSTRUCTURE;
+ else
+ return nextoffset;
+
+ case FDT_PROP:
+ return offset;
+ }
+ offset = nextoffset;
+ } while (tag == FDT_NOP);
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+int fdt_subnode_offset_namelen(const void *fdt, int offset,
+ const char *name, int namelen)
+{
+ int depth;
+
+ FDT_CHECK_HEADER(fdt);
+
+ for (depth = 0;
+ (offset >= 0) && (depth >= 0);
+ offset = fdt_next_node(fdt, offset, &depth))
+ if ((depth == 1)
+ && _fdt_nodename_eq(fdt, offset, name, namelen))
+ return offset;
+
+ if (depth < 0)
+ return -FDT_ERR_NOTFOUND;
+ return offset; /* error */
+}
+
+int fdt_subnode_offset(const void *fdt, int parentoffset,
+ const char *name)
+{
+ return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name));
+}
+
+int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen)
+{
+ const char *end = path + namelen;
+ const char *p = path;
+ int offset = 0;
+
+ FDT_CHECK_HEADER(fdt);
+
+ /* see if we have an alias */
+ if (*path != '/') {
+ const char *q = memchr(path, '/', end - p);
+
+ if (!q)
+ q = end;
+
+ p = fdt_get_alias_namelen(fdt, p, q - p);
+ if (!p)
+ return -FDT_ERR_BADPATH;
+ offset = fdt_path_offset(fdt, p);
+
+ p = q;
+ }
+
+ while (p < end) {
+ const char *q;
+
+ while (*p == '/') {
+ p++;
+ if (p == end)
+ return offset;
+ }
+ q = memchr(p, '/', end - p);
+ if (! q)
+ q = end;
+
+ offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p);
+ if (offset < 0)
+ return offset;
+
+ p = q;
+ }
+
+ return offset;
+}
+
+int fdt_path_offset(const void *fdt, const char *path)
+{
+ return fdt_path_offset_namelen(fdt, path, strlen(path));
+}
+
+const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
+{
+ const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset);
+ int err;
+
+ if (((err = fdt_check_header(fdt)) != 0)
+ || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0))
+ goto fail;
+
+ if (len)
+ *len = strlen(nh->name);
+
+ return nh->name;
+
+ fail:
+ if (len)
+ *len = err;
+ return NULL;
+}
+
+int fdt_first_property_offset(const void *fdt, int nodeoffset)
+{
+ int offset;
+
+ if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
+ return offset;
+
+ return _nextprop(fdt, offset);
+}
+
+int fdt_next_property_offset(const void *fdt, int offset)
+{
+ if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0)
+ return offset;
+
+ return _nextprop(fdt, offset);
+}
+
+const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
+ int offset,
+ int *lenp)
+{
+ int err;
+ const struct fdt_property *prop;
+
+ if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) {
+ if (lenp)
+ *lenp = err;
+ return NULL;
+ }
+
+ prop = _fdt_offset_ptr(fdt, offset);
+
+ if (lenp)
+ *lenp = fdt32_to_cpu(prop->len);
+
+ return prop;
+}
+
+const struct fdt_property *fdt_get_property_namelen(const void *fdt,
+ int offset,
+ const char *name,
+ int namelen, int *lenp)
+{
+ for (offset = fdt_first_property_offset(fdt, offset);
+ (offset >= 0);
+ (offset = fdt_next_property_offset(fdt, offset))) {
+ const struct fdt_property *prop;
+
+ if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) {
+ offset = -FDT_ERR_INTERNAL;
+ break;
+ }
+ if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff),
+ name, namelen))
+ return prop;
+ }
+
+ if (lenp)
+ *lenp = offset;
+ return NULL;
+}
+
+const struct fdt_property *fdt_get_property(const void *fdt,
+ int nodeoffset,
+ const char *name, int *lenp)
+{
+ return fdt_get_property_namelen(fdt, nodeoffset, name,
+ strlen(name), lenp);
+}
+
+const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
+ const char *name, int namelen, int *lenp)
+{
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp);
+ if (! prop)
+ return NULL;
+
+ return prop->data;
+}
+
+const void *fdt_getprop_by_offset(const void *fdt, int offset,
+ const char **namep, int *lenp)
+{
+ const struct fdt_property *prop;
+
+ prop = fdt_get_property_by_offset(fdt, offset, lenp);
+ if (!prop)
+ return NULL;
+ if (namep)
+ *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff));
+ return prop->data;
+}
+
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp);
+}
+
+uint32_t fdt_get_phandle(const void *fdt, int nodeoffset)
+{
+ const fdt32_t *php;
+ int len;
+
+ /* FIXME: This is a bit sub-optimal, since we potentially scan
+ * over all the properties twice. */
+ php = fdt_getprop(fdt, nodeoffset, "phandle", &len);
+ if (!php || (len != sizeof(*php))) {
+ php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len);
+ if (!php || (len != sizeof(*php)))
+ return 0;
+ }
+
+ return fdt32_to_cpu(*php);
+}
+
+const char *fdt_get_alias_namelen(const void *fdt,
+ const char *name, int namelen)
+{
+ int aliasoffset;
+
+ aliasoffset = fdt_path_offset(fdt, "/aliases");
+ if (aliasoffset < 0)
+ return NULL;
+
+ return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL);
+}
+
+const char *fdt_get_alias(const void *fdt, const char *name)
+{
+ return fdt_get_alias_namelen(fdt, name, strlen(name));
+}
+
+int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen)
+{
+ int pdepth = 0, p = 0;
+ int offset, depth, namelen;
+ const char *name;
+
+ FDT_CHECK_HEADER(fdt);
+
+ if (buflen < 2)
+ return -FDT_ERR_NOSPACE;
+
+ for (offset = 0, depth = 0;
+ (offset >= 0) && (offset <= nodeoffset);
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ while (pdepth > depth) {
+ do {
+ p--;
+ } while (buf[p-1] != '/');
+ pdepth--;
+ }
+
+ if (pdepth >= depth) {
+ name = fdt_get_name(fdt, offset, &namelen);
+ if (!name)
+ return namelen;
+ if ((p + namelen + 1) <= buflen) {
+ memcpy(buf + p, name, namelen);
+ p += namelen;
+ buf[p++] = '/';
+ pdepth++;
+ }
+ }
+
+ if (offset == nodeoffset) {
+ if (pdepth < (depth + 1))
+ return -FDT_ERR_NOSPACE;
+
+ if (p > 1) /* special case so that root path is "/", not "" */
+ p--;
+ buf[p] = '\0';
+ return 0;
+ }
+ }
+
+ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
+ return -FDT_ERR_BADOFFSET;
+ else if (offset == -FDT_ERR_BADOFFSET)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
+ int supernodedepth, int *nodedepth)
+{
+ int offset, depth;
+ int supernodeoffset = -FDT_ERR_INTERNAL;
+
+ FDT_CHECK_HEADER(fdt);
+
+ if (supernodedepth < 0)
+ return -FDT_ERR_NOTFOUND;
+
+ for (offset = 0, depth = 0;
+ (offset >= 0) && (offset <= nodeoffset);
+ offset = fdt_next_node(fdt, offset, &depth)) {
+ if (depth == supernodedepth)
+ supernodeoffset = offset;
+
+ if (offset == nodeoffset) {
+ if (nodedepth)
+ *nodedepth = depth;
+
+ if (supernodedepth > depth)
+ return -FDT_ERR_NOTFOUND;
+ else
+ return supernodeoffset;
+ }
+ }
+
+ if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0))
+ return -FDT_ERR_BADOFFSET;
+ else if (offset == -FDT_ERR_BADOFFSET)
+ return -FDT_ERR_BADSTRUCTURE;
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_node_depth(const void *fdt, int nodeoffset)
+{
+ int nodedepth;
+ int err;
+
+ err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth);
+ if (err)
+ return (err < 0) ? err : -FDT_ERR_INTERNAL;
+ return nodedepth;
+}
+
+int fdt_parent_offset(const void *fdt, int nodeoffset)
+{
+ int nodedepth = fdt_node_depth(fdt, nodeoffset);
+
+ if (nodedepth < 0)
+ return nodedepth;
+ return fdt_supernode_atdepth_offset(fdt, nodeoffset,
+ nodedepth - 1, NULL);
+}
+
+int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
+ const char *propname,
+ const void *propval, int proplen)
+{
+ int offset;
+ const void *val;
+ int len;
+
+ FDT_CHECK_HEADER(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we scan each
+ * property of a node in fdt_getprop(), then if that didn't
+ * find what we want, we scan over them again making our way
+ * to the next node. Still it's the easiest to implement
+ * approach; performance can come later. */
+ for (offset = fdt_next_node(fdt, startoffset, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ val = fdt_getprop(fdt, offset, propname, &len);
+ if (val && (len == proplen)
+ && (memcmp(val, propval, len) == 0))
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
+{
+ int offset;
+
+ if ((phandle == 0) || (phandle == -1))
+ return -FDT_ERR_BADPHANDLE;
+
+ FDT_CHECK_HEADER(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we
+ * potentially scan each property of a node in
+ * fdt_get_phandle(), then if that didn't find what
+ * we want, we scan over them again making our way to the next
+ * node. Still it's the easiest to implement approach;
+ * performance can come later. */
+ for (offset = fdt_next_node(fdt, -1, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ if (fdt_get_phandle(fdt, offset) == phandle)
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
+
+int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)
+{
+ int len = strlen(str);
+ const char *p;
+
+ while (listlen >= len) {
+ if (memcmp(str, strlist, len+1) == 0)
+ return 1;
+ p = memchr(strlist, '\0', listlen);
+ if (!p)
+ return 0; /* malformed strlist.. */
+ listlen -= (p-strlist) + 1;
+ strlist = p + 1;
+ }
+ return 0;
+}
+
+int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property)
+{
+ const char *list, *end;
+ int length, count = 0;
+
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
+ if (!list)
+ return length;
+
+ end = list + length;
+
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end)
+ return -FDT_ERR_BADVALUE;
+
+ list += length;
+ count++;
+ }
+
+ return count;
+}
+
+int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
+ const char *string)
+{
+ int length, len, idx = 0;
+ const char *list, *end;
+
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
+ if (!list)
+ return length;
+
+ len = strlen(string) + 1;
+ end = list + length;
+
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end)
+ return -FDT_ERR_BADVALUE;
+
+ if (length == len && memcmp(list, string, length) == 0)
+ return idx;
+
+ list += length;
+ idx++;
+ }
+
+ return -FDT_ERR_NOTFOUND;
+}
+
+const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
+ const char *property, int idx,
+ int *lenp)
+{
+ const char *list, *end;
+ int length;
+
+ list = fdt_getprop(fdt, nodeoffset, property, &length);
+ if (!list) {
+ if (lenp)
+ *lenp = length;
+
+ return NULL;
+ }
+
+ end = list + length;
+
+ while (list < end) {
+ length = strnlen(list, end - list) + 1;
+
+ /* Abort if the last string isn't properly NUL-terminated. */
+ if (list + length > end) {
+ if (lenp)
+ *lenp = -FDT_ERR_BADVALUE;
+
+ return NULL;
+ }
+
+ if (idx == 0) {
+ if (lenp)
+ *lenp = length - 1;
+
+ return list;
+ }
+
+ list += length;
+ idx--;
+ }
+
+ if (lenp)
+ *lenp = -FDT_ERR_NOTFOUND;
+
+ return NULL;
+}
+
+int fdt_node_check_compatible(const void *fdt, int nodeoffset,
+ const char *compatible)
+{
+ const void *prop;
+ int len;
+
+ prop = fdt_getprop(fdt, nodeoffset, "compatible", &len);
+ if (!prop)
+ return len;
+
+ return !fdt_stringlist_contains(prop, len, compatible);
+}
+
+int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
+ const char *compatible)
+{
+ int offset, err;
+
+ FDT_CHECK_HEADER(fdt);
+
+ /* FIXME: The algorithm here is pretty horrible: we scan each
+ * property of a node in fdt_node_check_compatible(), then if
+ * that didn't find what we want, we scan over them again
+ * making our way to the next node. Still it's the easiest to
+ * implement approach; performance can come later. */
+ for (offset = fdt_next_node(fdt, startoffset, NULL);
+ offset >= 0;
+ offset = fdt_next_node(fdt, offset, NULL)) {
+ err = fdt_node_check_compatible(fdt, offset, compatible);
+ if ((err < 0) && (err != -FDT_ERR_NOTFOUND))
+ return err;
+ else if (err == 0)
+ return offset;
+ }
+
+ return offset; /* error from fdt_next_node() */
+}
diff --git a/components/drivers/fdt/libfdt/fdt_rw.c b/components/drivers/fdt/libfdt/fdt_rw.c
new file mode 100644
index 0000000000..4406471b74
--- /dev/null
+++ b/components/drivers/fdt/libfdt/fdt_rw.c
@@ -0,0 +1,489 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+#include "fdt.h"
+#include "libfdt.h"
+#include "libfdt_internal.h"
+
+static int _fdt_blocks_misordered(const void *fdt,
+ int mem_rsv_size, int struct_size)
+{
+ return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8))
+ || (fdt_off_dt_struct(fdt) <
+ (fdt_off_mem_rsvmap(fdt) + mem_rsv_size))
+ || (fdt_off_dt_strings(fdt) <
+ (fdt_off_dt_struct(fdt) + struct_size))
+ || (fdt_totalsize(fdt) <
+ (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt)));
+}
+
+static int _fdt_rw_check_header(void *fdt)
+{
+ FDT_CHECK_HEADER(fdt);
+
+ if (fdt_version(fdt) < 17)
+ return -FDT_ERR_BADVERSION;
+ if (_fdt_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry),
+ fdt_size_dt_struct(fdt)))
+ return -FDT_ERR_BADLAYOUT;
+ if (fdt_version(fdt) > 17)
+ fdt_set_version(fdt, 17);
+
+ return 0;
+}
+
+#define FDT_RW_CHECK_HEADER(fdt) \
+ { \
+ int __err; \
+ if ((__err = _fdt_rw_check_header(fdt)) != 0) \
+ return __err; \
+ }
+
+static inline int _fdt_data_size(void *fdt)
+{
+ return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+}
+
+static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen)
+{
+ char *p = splicepoint;
+ char *end = (char *)fdt + _fdt_data_size(fdt);
+
+ if (((p + oldlen) < p) || ((p + oldlen) > end))
+ return -FDT_ERR_BADOFFSET;
+ if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt))
+ return -FDT_ERR_BADOFFSET;
+ if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt)))
+ return -FDT_ERR_NOSPACE;
+ memmove(p + newlen, p + oldlen, end - p - oldlen);
+ return 0;
+}
+
+static int _fdt_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p,
+ int oldn, int newn)
+{
+ int delta = (newn - oldn) * sizeof(*p);
+ int err;
+ err = _fdt_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p));
+ if (err)
+ return err;
+ fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta);
+ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
+ return 0;
+}
+
+static int _fdt_splice_struct(void *fdt, void *p,
+ int oldlen, int newlen)
+{
+ int delta = newlen - oldlen;
+ int err;
+
+ if ((err = _fdt_splice(fdt, p, oldlen, newlen)))
+ return err;
+
+ fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta);
+ fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta);
+ return 0;
+}
+
+static int _fdt_splice_string(void *fdt, int newlen)
+{
+ void *p = (char *)fdt
+ + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
+ int err;
+
+ if ((err = _fdt_splice(fdt, p, 0, newlen)))
+ return err;
+
+ fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen);
+ return 0;
+}
+
+static int _fdt_find_add_string(void *fdt, const char *s)
+{
+ char *strtab = (char *)fdt + fdt_off_dt_strings(fdt);
+ const char *p;
+ char *new;
+ int len = strlen(s) + 1;
+ int err;
+
+ p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s);
+ if (p)
+ /* found it */
+ return (p - strtab);
+
+ new = strtab + fdt_size_dt_strings(fdt);
+ err = _fdt_splice_string(fdt, len);
+ if (err)
+ return err;
+
+ memcpy(new, s, len);
+ return (new - strtab);
+}
+
+int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size)
+{
+ struct fdt_reserve_entry *re;
+ int err;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt));
+ err = _fdt_splice_mem_rsv(fdt, re, 0, 1);
+ if (err)
+ return err;
+
+ re->address = cpu_to_fdt64(address);
+ re->size = cpu_to_fdt64(size);
+ return 0;
+}
+
+int fdt_del_mem_rsv(void *fdt, int n)
+{
+ struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n);
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ if (n >= fdt_num_mem_rsv(fdt))
+ return -FDT_ERR_NOTFOUND;
+
+ return _fdt_splice_mem_rsv(fdt, re, 1, 0);
+}
+
+static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name,
+ int len, struct fdt_property **prop)
+{
+ int oldlen;
+ int err;
+
+ *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
+ if (! (*prop))
+ return oldlen;
+
+ if ((err = _fdt_splice_struct(fdt, (*prop)->data, FDT_TAGALIGN(oldlen),
+ FDT_TAGALIGN(len))))
+ return err;
+
+ (*prop)->len = cpu_to_fdt32(len);
+ return 0;
+}
+
+static int _fdt_add_property(void *fdt, int nodeoffset, const char *name,
+ int len, struct fdt_property **prop)
+{
+ int proplen;
+ int nextoffset;
+ int namestroff;
+ int err;
+
+ if ((nextoffset = _fdt_check_node_offset(fdt, nodeoffset)) < 0)
+ return nextoffset;
+
+ namestroff = _fdt_find_add_string(fdt, name);
+ if (namestroff < 0)
+ return namestroff;
+
+ *prop = _fdt_offset_ptr_w(fdt, nextoffset);
+ proplen = sizeof(**prop) + FDT_TAGALIGN(len);
+
+ err = _fdt_splice_struct(fdt, *prop, 0, proplen);
+ if (err)
+ return err;
+
+ (*prop)->tag = cpu_to_fdt32(FDT_PROP);
+ (*prop)->nameoff = cpu_to_fdt32(namestroff);
+ (*prop)->len = cpu_to_fdt32(len);
+ return 0;
+}
+
+int fdt_set_name(void *fdt, int nodeoffset, const char *name)
+{
+ char *namep;
+ int oldlen, newlen;
+ int err;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen);
+ if (!namep)
+ return oldlen;
+
+ newlen = strlen(name);
+
+ err = _fdt_splice_struct(fdt, namep, FDT_TAGALIGN(oldlen+1),
+ FDT_TAGALIGN(newlen+1));
+ if (err)
+ return err;
+
+ memcpy(namep, name, newlen+1);
+ return 0;
+}
+
+int fdt_setprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ struct fdt_property *prop;
+ int err;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ err = _fdt_resize_property(fdt, nodeoffset, name, len, &prop);
+ if (err == -FDT_ERR_NOTFOUND)
+ err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);
+ if (err)
+ return err;
+
+ if (len)
+ memcpy(prop->data, val, len);
+ return 0;
+}
+
+int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ struct fdt_property *prop;
+ int err, oldlen, newlen;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen);
+ if (prop) {
+ newlen = len + oldlen;
+ err = _fdt_splice_struct(fdt, prop->data,
+ FDT_TAGALIGN(oldlen),
+ FDT_TAGALIGN(newlen));
+ if (err)
+ return err;
+ prop->len = cpu_to_fdt32(newlen);
+ memcpy(prop->data + oldlen, val, len);
+ } else {
+ err = _fdt_add_property(fdt, nodeoffset, name, len, &prop);
+ if (err)
+ return err;
+ memcpy(prop->data, val, len);
+ }
+ return 0;
+}
+
+int fdt_delprop(void *fdt, int nodeoffset, const char *name)
+{
+ struct fdt_property *prop;
+ int len, proplen;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
+ if (! prop)
+ return len;
+
+ proplen = sizeof(*prop) + FDT_TAGALIGN(len);
+ return _fdt_splice_struct(fdt, prop, proplen, 0);
+}
+
+int fdt_add_subnode_namelen(void *fdt, int parentoffset,
+ const char *name, int namelen)
+{
+ struct fdt_node_header *nh;
+ int offset, nextoffset;
+ int nodelen;
+ int err;
+ uint32_t tag;
+ fdt32_t *endtag;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen);
+ if (offset >= 0)
+ return -FDT_ERR_EXISTS;
+ else if (offset != -FDT_ERR_NOTFOUND)
+ return offset;
+
+ /* Try to place the new node after the parent's properties */
+ fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */
+ do {
+ offset = nextoffset;
+ tag = fdt_next_tag(fdt, offset, &nextoffset);
+ } while ((tag == FDT_PROP) || (tag == FDT_NOP));
+
+ nh = _fdt_offset_ptr_w(fdt, offset);
+ nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE;
+
+ err = _fdt_splice_struct(fdt, nh, 0, nodelen);
+ if (err)
+ return err;
+
+ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
+ memset(nh->name, 0, FDT_TAGALIGN(namelen+1));
+ memcpy(nh->name, name, namelen);
+ endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE);
+ *endtag = cpu_to_fdt32(FDT_END_NODE);
+
+ return offset;
+}
+
+int fdt_add_subnode(void *fdt, int parentoffset, const char *name)
+{
+ return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name));
+}
+
+int fdt_del_node(void *fdt, int nodeoffset)
+{
+ int endoffset;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ endoffset = _fdt_node_end_offset(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ return _fdt_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset),
+ endoffset - nodeoffset, 0);
+}
+
+static void _fdt_packblocks(const char *old, char *new,
+ int mem_rsv_size, int struct_size)
+{
+ int mem_rsv_off, struct_off, strings_off;
+
+ mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8);
+ struct_off = mem_rsv_off + mem_rsv_size;
+ strings_off = struct_off + struct_size;
+
+ memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size);
+ fdt_set_off_mem_rsvmap(new, mem_rsv_off);
+
+ memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size);
+ fdt_set_off_dt_struct(new, struct_off);
+ fdt_set_size_dt_struct(new, struct_size);
+
+ memmove(new + strings_off, old + fdt_off_dt_strings(old),
+ fdt_size_dt_strings(old));
+ fdt_set_off_dt_strings(new, strings_off);
+ fdt_set_size_dt_strings(new, fdt_size_dt_strings(old));
+}
+
+int fdt_open_into(const void *fdt, void *buf, int bufsize)
+{
+ int err;
+ int mem_rsv_size, struct_size;
+ int newsize;
+ const char *fdtstart = fdt;
+ const char *fdtend = fdtstart + fdt_totalsize(fdt);
+ char *tmp;
+
+ FDT_CHECK_HEADER(fdt);
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+
+ if (fdt_version(fdt) >= 17) {
+ struct_size = fdt_size_dt_struct(fdt);
+ } else {
+ struct_size = 0;
+ while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END)
+ ;
+ if (struct_size < 0)
+ return struct_size;
+ }
+
+ if (!_fdt_blocks_misordered(fdt, mem_rsv_size, struct_size)) {
+ /* no further work necessary */
+ err = fdt_move(fdt, buf, bufsize);
+ if (err)
+ return err;
+ fdt_set_version(buf, 17);
+ fdt_set_size_dt_struct(buf, struct_size);
+ fdt_set_totalsize(buf, bufsize);
+ return 0;
+ }
+
+ /* Need to reorder */
+ newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size
+ + struct_size + fdt_size_dt_strings(fdt);
+
+ if (bufsize < newsize)
+ return -FDT_ERR_NOSPACE;
+
+ /* First attempt to build converted tree at beginning of buffer */
+ tmp = buf;
+ /* But if that overlaps with the old tree... */
+ if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) {
+ /* Try right after the old tree instead */
+ tmp = (char *)(uintptr_t)fdtend;
+ if ((tmp + newsize) > ((char *)buf + bufsize))
+ return -FDT_ERR_NOSPACE;
+ }
+
+ _fdt_packblocks(fdt, tmp, mem_rsv_size, struct_size);
+ memmove(buf, tmp, newsize);
+
+ fdt_set_magic(buf, FDT_MAGIC);
+ fdt_set_totalsize(buf, bufsize);
+ fdt_set_version(buf, 17);
+ fdt_set_last_comp_version(buf, 16);
+ fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt));
+
+ return 0;
+}
+
+int fdt_pack(void *fdt)
+{
+ int mem_rsv_size;
+
+ FDT_RW_CHECK_HEADER(fdt);
+
+ mem_rsv_size = (fdt_num_mem_rsv(fdt)+1)
+ * sizeof(struct fdt_reserve_entry);
+ _fdt_packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt));
+ fdt_set_totalsize(fdt, _fdt_data_size(fdt));
+
+ return 0;
+}
diff --git a/components/drivers/fdt/libfdt/fdt_strerror.c b/components/drivers/fdt/libfdt/fdt_strerror.c
new file mode 100644
index 0000000000..eda657ff07
--- /dev/null
+++ b/components/drivers/fdt/libfdt/fdt_strerror.c
@@ -0,0 +1,100 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+#include "fdt.h"
+#include "libfdt.h"
+#include "libfdt_internal.h"
+
+struct fdt_errtabent {
+ const char *str;
+};
+
+#define FDT_ERRTABENT(val) \
+ [(val)] = { .str = #val, }
+
+static struct fdt_errtabent fdt_errtable[] = {
+ FDT_ERRTABENT(FDT_ERR_NOTFOUND),
+ FDT_ERRTABENT(FDT_ERR_EXISTS),
+ FDT_ERRTABENT(FDT_ERR_NOSPACE),
+
+ FDT_ERRTABENT(FDT_ERR_BADOFFSET),
+ FDT_ERRTABENT(FDT_ERR_BADPATH),
+ FDT_ERRTABENT(FDT_ERR_BADPHANDLE),
+ FDT_ERRTABENT(FDT_ERR_BADSTATE),
+
+ FDT_ERRTABENT(FDT_ERR_TRUNCATED),
+ FDT_ERRTABENT(FDT_ERR_BADMAGIC),
+ FDT_ERRTABENT(FDT_ERR_BADVERSION),
+ FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE),
+ FDT_ERRTABENT(FDT_ERR_BADLAYOUT),
+ FDT_ERRTABENT(FDT_ERR_INTERNAL),
+ FDT_ERRTABENT(FDT_ERR_BADNCELLS),
+ FDT_ERRTABENT(FDT_ERR_BADVALUE),
+ FDT_ERRTABENT(FDT_ERR_BADOVERLAY),
+ FDT_ERRTABENT(FDT_ERR_NOPHANDLES),
+};
+#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0]))
+
+const char *fdt_strerror(int errval)
+{
+ if (errval > 0)
+ return "<valid offset/length>";
+ else if (errval == 0)
+ return "<no error>";
+ else if (errval > -FDT_ERRTABSIZE) {
+ const char *s = fdt_errtable[-errval].str;
+
+ if (s)
+ return s;
+ }
+
+ return "<unknown error>";
+}
diff --git a/components/drivers/fdt/libfdt/fdt_sw.c b/components/drivers/fdt/libfdt/fdt_sw.c
new file mode 100644
index 0000000000..5d6de1b1b2
--- /dev/null
+++ b/components/drivers/fdt/libfdt/fdt_sw.c
@@ -0,0 +1,286 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+#include "fdt.h"
+#include "libfdt.h"
+#include "libfdt_internal.h"
+
+static int _fdt_sw_check_header(void *fdt)
+{
+ if (fdt_magic(fdt) != FDT_SW_MAGIC)
+ return -FDT_ERR_BADMAGIC;
+ /* FIXME: should check more details about the header state */
+ return 0;
+}
+
+#define FDT_SW_CHECK_HEADER(fdt) \
+ { \
+ int err; \
+ if ((err = _fdt_sw_check_header(fdt)) != 0) \
+ return err; \
+ }
+
+static void *_fdt_grab_space(void *fdt, size_t len)
+{
+ int offset = fdt_size_dt_struct(fdt);
+ int spaceleft;
+
+ spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt)
+ - fdt_size_dt_strings(fdt);
+
+ if ((offset + len < offset) || (offset + len > spaceleft))
+ return NULL;
+
+ fdt_set_size_dt_struct(fdt, offset + len);
+ return _fdt_offset_ptr_w(fdt, offset);
+}
+
+int fdt_create(void *buf, int bufsize)
+{
+ void *fdt = buf;
+
+ if (bufsize < sizeof(struct fdt_header))
+ return -FDT_ERR_NOSPACE;
+
+ memset(buf, 0, bufsize);
+
+ fdt_set_magic(fdt, FDT_SW_MAGIC);
+ fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION);
+ fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION);
+ fdt_set_totalsize(fdt, bufsize);
+
+ fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header),
+ sizeof(struct fdt_reserve_entry)));
+ fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt));
+ fdt_set_off_dt_strings(fdt, bufsize);
+
+ return 0;
+}
+
+int fdt_resize(void *fdt, void *buf, int bufsize)
+{
+ size_t headsize, tailsize;
+ char *oldtail, *newtail;
+
+ FDT_SW_CHECK_HEADER(fdt);
+
+ headsize = fdt_off_dt_struct(fdt);
+ tailsize = fdt_size_dt_strings(fdt);
+
+ if ((headsize + tailsize) > bufsize)
+ return -FDT_ERR_NOSPACE;
+
+ oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize;
+ newtail = (char *)buf + bufsize - tailsize;
+
+ /* Two cases to avoid clobbering data if the old and new
+ * buffers partially overlap */
+ if (buf <= fdt) {
+ memmove(buf, fdt, headsize);
+ memmove(newtail, oldtail, tailsize);
+ } else {
+ memmove(newtail, oldtail, tailsize);
+ memmove(buf, fdt, headsize);
+ }
+
+ fdt_set_off_dt_strings(buf, bufsize);
+ fdt_set_totalsize(buf, bufsize);
+
+ return 0;
+}
+
+int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size)
+{
+ struct fdt_reserve_entry *re;
+ int offset;
+
+ FDT_SW_CHECK_HEADER(fdt);
+
+ if (fdt_size_dt_struct(fdt))
+ return -FDT_ERR_BADSTATE;
+
+ offset = fdt_off_dt_struct(fdt);
+ if ((offset + sizeof(*re)) > fdt_totalsize(fdt))
+ return -FDT_ERR_NOSPACE;
+
+ re = (struct fdt_reserve_entry *)((char *)fdt + offset);
+ re->address = cpu_to_fdt64(addr);
+ re->size = cpu_to_fdt64(size);
+
+ fdt_set_off_dt_struct(fdt, offset + sizeof(*re));
+
+ return 0;
+}
+
+int fdt_finish_reservemap(void *fdt)
+{
+ return fdt_add_reservemap_entry(fdt, 0, 0);
+}
+
+int fdt_begin_node(void *fdt, const char *name)
+{
+ struct fdt_node_header *nh;
+ int namelen = strlen(name) + 1;
+
+ FDT_SW_CHECK_HEADER(fdt);
+
+ nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen));
+ if (! nh)
+ return -FDT_ERR_NOSPACE;
+
+ nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE);
+ memcpy(nh->name, name, namelen);
+ return 0;
+}
+
+int fdt_end_node(void *fdt)
+{
+ fdt32_t *en;
+
+ FDT_SW_CHECK_HEADER(fdt);
+
+ en = _fdt_grab_space(fdt, FDT_TAGSIZE);
+ if (! en)
+ return -FDT_ERR_NOSPACE;
+
+ *en = cpu_to_fdt32(FDT_END_NODE);
+ return 0;
+}
+
+static int _fdt_find_add_string(void *fdt, const char *s)
+{
+ char *strtab = (char *)fdt + fdt_totalsize(fdt);
+ const char *p;
+ int strtabsize = fdt_size_dt_strings(fdt);
+ int len = strlen(s) + 1;
+ int struct_top, offset;
+
+ p = _fdt_find_string(strtab - strtabsize, strtabsize, s);
+ if (p)
+ return p - strtab;
+
+ /* Add it */
+ offset = -strtabsize - len;
+ struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ if (fdt_totalsize(fdt) + offset < struct_top)
+ return 0; /* no more room :( */
+
+ memcpy(strtab + offset, s, len);
+ fdt_set_size_dt_strings(fdt, strtabsize + len);
+ return offset;
+}
+
+int fdt_property(void *fdt, const char *name, const void *val, int len)
+{
+ struct fdt_property *prop;
+ int nameoff;
+
+ FDT_SW_CHECK_HEADER(fdt);
+
+ nameoff = _fdt_find_add_string(fdt, name);
+ if (nameoff == 0)
+ return -FDT_ERR_NOSPACE;
+
+ prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len));
+ if (! prop)
+ return -FDT_ERR_NOSPACE;
+
+ prop->tag = cpu_to_fdt32(FDT_PROP);
+ prop->nameoff = cpu_to_fdt32(nameoff);
+ prop->len = cpu_to_fdt32(len);
+ memcpy(prop->data, val, len);
+ return 0;
+}
+
+int fdt_finish(void *fdt)
+{
+ char *p = (char *)fdt;
+ fdt32_t *end;
+ int oldstroffset, newstroffset;
+ uint32_t tag;
+ int offset, nextoffset;
+
+ FDT_SW_CHECK_HEADER(fdt);
+
+ /* Add terminator */
+ end = _fdt_grab_space(fdt, sizeof(*end));
+ if (! end)
+ return -FDT_ERR_NOSPACE;
+ *end = cpu_to_fdt32(FDT_END);
+
+ /* Relocate the string table */
+ oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt);
+ newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt);
+ memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt));
+ fdt_set_off_dt_strings(fdt, newstroffset);
+
+ /* Walk the structure, correcting string offsets */
+ offset = 0;
+ while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) {
+ if (tag == FDT_PROP) {
+ struct fdt_property *prop =
+ _fdt_offset_ptr_w(fdt, offset);
+ int nameoff;
+
+ nameoff = fdt32_to_cpu(prop->nameoff);
+ nameoff += fdt_size_dt_strings(fdt);
+ prop->nameoff = cpu_to_fdt32(nameoff);
+ }
+ offset = nextoffset;
+ }
+ if (nextoffset < 0)
+ return nextoffset;
+
+ /* Finally, adjust the header */
+ fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt));
+ fdt_set_magic(fdt, FDT_MAGIC);
+ return 0;
+}
diff --git a/components/drivers/fdt/libfdt/fdt_wip.c b/components/drivers/fdt/libfdt/fdt_wip.c
new file mode 100644
index 0000000000..a9f2d66679
--- /dev/null
+++ b/components/drivers/fdt/libfdt/fdt_wip.c
@@ -0,0 +1,137 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "libfdt_env.h"
+#include "fdt.h"
+#include "libfdt.h"
+#include "libfdt_internal.h"
+
+int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
+ const char *name, int namelen,
+ uint32_t idx, const void *val,
+ int len)
+{
+ void *propval;
+ int proplen;
+
+ propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen,
+ &proplen);
+ if (!propval)
+ return proplen;
+
+ if (proplen < (len + idx))
+ return -FDT_ERR_NOSPACE;
+
+ memcpy((char *)propval + idx, val, len);
+ return 0;
+}
+
+int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len)
+{
+ const void *propval;
+ int proplen;
+
+ propval = fdt_getprop(fdt, nodeoffset, name, &proplen);
+ if (! propval)
+ return proplen;
+
+ if (proplen != len)
+ return -FDT_ERR_NOSPACE;
+
+ return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name,
+ strlen(name), 0,
+ val, len);
+}
+
+static void _fdt_nop_region(void *start, int len)
+{
+ fdt32_t *p;
+
+ for (p = start; (char *)p < ((char *)start + len); p++)
+ *p = cpu_to_fdt32(FDT_NOP);
+}
+
+int fdt_nop_property(void *fdt, int nodeoffset, const char *name)
+{
+ struct fdt_property *prop;
+ int len;
+
+ prop = fdt_get_property_w(fdt, nodeoffset, name, &len);
+ if (! prop)
+ return len;
+
+ _fdt_nop_region(prop, len + sizeof(*prop));
+
+ return 0;
+}
+
+int _fdt_node_end_offset(void *fdt, int offset)
+{
+ int depth = 0;
+
+ while ((offset >= 0) && (depth >= 0))
+ offset = fdt_next_node(fdt, offset, &depth);
+
+ return offset;
+}
+
+int fdt_nop_node(void *fdt, int nodeoffset)
+{
+ int endoffset;
+
+ endoffset = _fdt_node_end_offset(fdt, nodeoffset);
+ if (endoffset < 0)
+ return endoffset;
+
+ _fdt_nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0),
+ endoffset - nodeoffset);
+ return 0;
+}
diff --git a/components/drivers/fdt/libfdt/libfdt.h b/components/drivers/fdt/libfdt/libfdt.h
new file mode 100644
index 0000000000..05de18d721
--- /dev/null
+++ b/components/drivers/fdt/libfdt/libfdt.h
@@ -0,0 +1,1876 @@
+#ifndef _LIBFDT_H
+#define _LIBFDT_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "libfdt_env.h"
+#include "fdt.h"
+
+#define FDT_FIRST_SUPPORTED_VERSION 0x10
+#define FDT_LAST_SUPPORTED_VERSION 0x11
+
+/* Error codes: informative error codes */
+#define FDT_ERR_NOTFOUND 1
+ /* FDT_ERR_NOTFOUND: The requested node or property does not exist */
+#define FDT_ERR_EXISTS 2
+ /* FDT_ERR_EXISTS: Attempted to create a node or property which
+ * already exists */
+#define FDT_ERR_NOSPACE 3
+ /* FDT_ERR_NOSPACE: Operation needed to expand the device
+ * tree, but its buffer did not have sufficient space to
+ * contain the expanded tree. Use fdt_open_into() to move the
+ * device tree to a buffer with more space. */
+
+/* Error codes: codes for bad parameters */
+#define FDT_ERR_BADOFFSET 4
+ /* FDT_ERR_BADOFFSET: Function was passed a structure block
+ * offset which is out-of-bounds, or which points to an
+ * unsuitable part of the structure for the operation. */
+#define FDT_ERR_BADPATH 5
+ /* FDT_ERR_BADPATH: Function was passed a badly formatted path
+ * (e.g. missing a leading / for a function which requires an
+ * absolute path) */
+#define FDT_ERR_BADPHANDLE 6
+ /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle.
+ * This can be caused either by an invalid phandle property
+ * length, or the phandle value was either 0 or -1, which are
+ * not permitted. */
+#define FDT_ERR_BADSTATE 7
+ /* FDT_ERR_BADSTATE: Function was passed an incomplete device
+ * tree created by the sequential-write functions, which is
+ * not sufficiently complete for the requested operation. */
+
+/* Error codes: codes for bad device tree blobs */
+#define FDT_ERR_TRUNCATED 8
+ /* FDT_ERR_TRUNCATED: Structure block of the given device tree
+ * ends without an FDT_END tag. */
+#define FDT_ERR_BADMAGIC 9
+ /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a
+ * device tree at all - it is missing the flattened device
+ * tree magic number. */
+#define FDT_ERR_BADVERSION 10
+ /* FDT_ERR_BADVERSION: Given device tree has a version which
+ * can't be handled by the requested operation. For
+ * read-write functions, this may mean that fdt_open_into() is
+ * required to convert the tree to the expected version. */
+#define FDT_ERR_BADSTRUCTURE 11
+ /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt
+ * structure block or other serious error (e.g. misnested
+ * nodes, or subnodes preceding properties). */
+#define FDT_ERR_BADLAYOUT 12
+ /* FDT_ERR_BADLAYOUT: For read-write functions, the given
+ * device tree has it's sub-blocks in an order that the
+ * function can't handle (memory reserve map, then structure,
+ * then strings). Use fdt_open_into() to reorganize the tree
+ * into a form suitable for the read-write operations. */
+
+/* "Can't happen" error indicating a bug in libfdt */
+#define FDT_ERR_INTERNAL 13
+ /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion.
+ * Should never be returned, if it is, it indicates a bug in
+ * libfdt itself. */
+
+/* Errors in device tree content */
+#define FDT_ERR_BADNCELLS 14
+ /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells
+ * or similar property with a bad format or value */
+
+#define FDT_ERR_BADVALUE 15
+ /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected
+ * value. For example: a property expected to contain a string list
+ * is not NUL-terminated within the length of its value. */
+
+#define FDT_ERR_BADOVERLAY 16
+ /* FDT_ERR_BADOVERLAY: The device tree overlay, while
+ * correctly structured, cannot be applied due to some
+ * unexpected or missing value, property or node. */
+
+#define FDT_ERR_NOPHANDLES 17
+ /* FDT_ERR_NOPHANDLES: The device tree doesn't have any
+ * phandle available anymore without causing an overflow */
+
+#define FDT_ERR_MAX 17
+
+/**********************************************************************/
+/* Low-level functions (you probably don't need these) */
+/**********************************************************************/
+
+#ifndef SWIG /* This function is not useful in Python */
+const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen);
+#endif
+static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen)
+{
+ return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen);
+}
+
+uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset);
+
+static inline void fdt32_st(void *property, uint32_t value)
+{
+ uint8_t *bp = (uint8_t *)property;
+
+ bp[0] = value >> 24;
+ bp[1] = (value >> 16) & 0xff;
+ bp[2] = (value >> 8) & 0xff;
+ bp[3] = value & 0xff;
+}
+
+static inline void fdt64_st(void *property, uint64_t value)
+{
+ uint8_t *bp = (uint8_t *)property;
+
+ bp[0] = value >> 56;
+ bp[1] = (value >> 48) & 0xff;
+ bp[2] = (value >> 40) & 0xff;
+ bp[3] = (value >> 32) & 0xff;
+ bp[4] = (value >> 24) & 0xff;
+ bp[5] = (value >> 16) & 0xff;
+ bp[6] = (value >> 8) & 0xff;
+ bp[7] = value & 0xff;
+}
+
+/**********************************************************************/
+/* Traversal functions */
+/**********************************************************************/
+
+int fdt_next_node(const void *fdt, int offset, int *depth);
+
+/**
+ * fdt_first_subnode() - get offset of first direct subnode
+ *
+ * @fdt: FDT blob
+ * @offset: Offset of node to check
+ * @return offset of first subnode, or -FDT_ERR_NOTFOUND if there is none
+ */
+int fdt_first_subnode(const void *fdt, int offset);
+
+/**
+ * fdt_next_subnode() - get offset of next direct subnode
+ *
+ * After first calling fdt_first_subnode(), call this function repeatedly to
+ * get direct subnodes of a parent node.
+ *
+ * @fdt: FDT blob
+ * @offset: Offset of previous subnode
+ * @return offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more
+ * subnodes
+ */
+int fdt_next_subnode(const void *fdt, int offset);
+
+/**
+ * fdt_for_each_subnode - iterate over all subnodes of a parent
+ *
+ * @node: child node (int, lvalue)
+ * @fdt: FDT blob (const void *)
+ * @parent: parent node (int)
+ *
+ * This is actually a wrapper around a for loop and would be used like so:
+ *
+ * fdt_for_each_subnode(node, fdt, parent) {
+ * Use node
+ * ...
+ * }
+ *
+ * if ((node < 0) && (node != -FDT_ERR_NOT_FOUND)) {
+ * Error handling
+ * }
+ *
+ * Note that this is implemented as a macro and @node is used as
+ * iterator in the loop. The parent variable be constant or even a
+ * literal.
+ *
+ */
+#define fdt_for_each_subnode(node, fdt, parent) \
+ for (node = fdt_first_subnode(fdt, parent); \
+ node >= 0; \
+ node = fdt_next_subnode(fdt, node))
+
+/**********************************************************************/
+/* General functions */
+/**********************************************************************/
+#define fdt_get_header(fdt, field) \
+ (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field))
+#define fdt_magic(fdt) (fdt_get_header(fdt, magic))
+#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize))
+#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct))
+#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings))
+#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap))
+#define fdt_version(fdt) (fdt_get_header(fdt, version))
+#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version))
+#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys))
+#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings))
+#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct))
+
+#define __fdt_set_hdr(name) \
+ static inline void fdt_set_##name(void *fdt, uint32_t val) \
+ { \
+ struct fdt_header *fdth = (struct fdt_header *)fdt; \
+ fdth->name = cpu_to_fdt32(val); \
+ }
+__fdt_set_hdr(magic);
+__fdt_set_hdr(totalsize);
+__fdt_set_hdr(off_dt_struct);
+__fdt_set_hdr(off_dt_strings);
+__fdt_set_hdr(off_mem_rsvmap);
+__fdt_set_hdr(version);
+__fdt_set_hdr(last_comp_version);
+__fdt_set_hdr(boot_cpuid_phys);
+__fdt_set_hdr(size_dt_strings);
+__fdt_set_hdr(size_dt_struct);
+#undef __fdt_set_hdr
+
+/**
+ * fdt_check_header - sanity check a device tree or possible device tree
+ * @fdt: pointer to data which might be a flattened device tree
+ *
+ * fdt_check_header() checks that the given buffer contains what
+ * appears to be a flattened device tree with sane information in its
+ * header.
+ *
+ * returns:
+ * 0, if the buffer appears to contain a valid device tree
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings, as above
+ */
+int fdt_check_header(const void *fdt);
+
+/**
+ * fdt_move - move a device tree around in memory
+ * @fdt: pointer to the device tree to move
+ * @buf: pointer to memory where the device is to be moved
+ * @bufsize: size of the memory space at buf
+ *
+ * fdt_move() relocates, if possible, the device tree blob located at
+ * fdt to the buffer at buf of size bufsize. The buffer may overlap
+ * with the existing device tree blob at fdt. Therefore,
+ * fdt_move(fdt, fdt, fdt_totalsize(fdt))
+ * should always succeed.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_move(const void *fdt, void *buf, int bufsize);
+
+/**********************************************************************/
+/* Read-only functions */
+/**********************************************************************/
+
+/**
+ * fdt_string - retrieve a string from the strings block of a device tree
+ * @fdt: pointer to the device tree blob
+ * @stroffset: offset of the string within the strings block (native endian)
+ *
+ * fdt_string() retrieves a pointer to a single string from the
+ * strings block of the device tree blob at fdt.
+ *
+ * returns:
+ * a pointer to the string, on success
+ * NULL, if stroffset is out of bounds
+ */
+const char *fdt_string(const void *fdt, int stroffset);
+
+/**
+ * fdt_get_max_phandle - retrieves the highest phandle in a tree
+ * @fdt: pointer to the device tree blob
+ *
+ * fdt_get_max_phandle retrieves the highest phandle in the given
+ * device tree. This will ignore badly formatted phandles, or phandles
+ * with a value of 0 or -1.
+ *
+ * returns:
+ * the highest phandle on success
+ * 0, if no phandle was found in the device tree
+ * -1, if an error occurred
+ */
+uint32_t fdt_get_max_phandle(const void *fdt);
+
+/**
+ * fdt_num_mem_rsv - retrieve the number of memory reserve map entries
+ * @fdt: pointer to the device tree blob
+ *
+ * Returns the number of entries in the device tree blob's memory
+ * reservation map. This does not include the terminating 0,0 entry
+ * or any other (0,0) entries reserved for expansion.
+ *
+ * returns:
+ * the number of entries
+ */
+int fdt_num_mem_rsv(const void *fdt);
+
+/**
+ * fdt_get_mem_rsv - retrieve one memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @address, @size: pointers to 64-bit variables
+ *
+ * On success, *address and *size will contain the address and size of
+ * the n-th reserve map entry from the device tree blob, in
+ * native-endian format.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size);
+
+/**
+ * fdt_subnode_offset_namelen - find a subnode based on substring
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_subnode_offset(), but only examine the first
+ * namelen characters of name for matching the subnode name. This is
+ * useful for finding subnodes based on a portion of a larger string,
+ * such as a full path.
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_subnode_offset_namelen(const void *fdt, int parentoffset,
+ const char *name, int namelen);
+#endif
+/**
+ * fdt_subnode_offset - find a subnode of a given node
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ *
+ * fdt_subnode_offset() finds a subnode of the node at structure block
+ * offset parentoffset with the given name. name may include a unit
+ * address, in which case fdt_subnode_offset() will find the subnode
+ * with that unit address, or the unit address may be omitted, in
+ * which case fdt_subnode_offset() will find an arbitrary subnode
+ * whose name excluding unit address matches the given name.
+ *
+ * returns:
+ * structure block offset of the requested subnode (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
+ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
+ * tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name);
+
+/**
+ * fdt_path_offset_namelen - find a tree node by its full path
+ * @fdt: pointer to the device tree blob
+ * @path: full path of the node to locate
+ * @namelen: number of characters of path to consider
+ *
+ * Identical to fdt_path_offset(), but only consider the first namelen
+ * characters of path as the path name.
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen);
+#endif
+
+/**
+ * fdt_path_offset - find a tree node by its full path
+ * @fdt: pointer to the device tree blob
+ * @path: full path of the node to locate
+ *
+ * fdt_path_offset() finds a node of a given path in the device tree.
+ * Each path component may omit the unit address portion, but the
+ * results of this are undefined if any such path component is
+ * ambiguous (that is if there are multiple nodes at the relevant
+ * level matching the given component, differentiated only by unit
+ * address).
+ *
+ * returns:
+ * structure block offset of the node with the requested path (>=0), on
+ * success
+ * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid
+ * -FDT_ERR_NOTFOUND, if the requested node does not exist
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_path_offset(const void *fdt, const char *path);
+
+/**
+ * fdt_get_name - retrieve the name of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of the starting node
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_name() retrieves the name (including unit address) of the
+ * device tree node at structure block offset nodeoffset. If lenp is
+ * non-NULL, the length of this name is also returned, in the integer
+ * pointed to by lenp.
+ *
+ * returns:
+ * pointer to the node's name, on success
+ * If lenp is non-NULL, *lenp contains the length of that name
+ * (>=0)
+ * NULL, on error
+ * if lenp is non-NULL *lenp contains an error code (<0):
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
+ * tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp);
+
+/**
+ * fdt_first_property_offset - find the offset of a node's first property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of a node
+ *
+ * fdt_first_property_offset() finds the first property of the node at
+ * the given structure block offset.
+ *
+ * returns:
+ * structure block offset of the property (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the requested node has no properties
+ * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_first_property_offset(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_next_property_offset - step through a node's properties
+ * @fdt: pointer to the device tree blob
+ * @offset: structure block offset of a property
+ *
+ * fdt_next_property_offset() finds the property immediately after the
+ * one at the given structure block offset. This will be a property
+ * of the same node as the given property.
+ *
+ * returns:
+ * structure block offset of the next property (>=0), on success
+ * -FDT_ERR_NOTFOUND, if the given property is the last in its node
+ * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_next_property_offset(const void *fdt, int offset);
+
+/**
+ * fdt_for_each_property_offset - iterate over all properties of a node
+ *
+ * @property_offset: property offset (int, lvalue)
+ * @fdt: FDT blob (const void *)
+ * @node: node offset (int)
+ *
+ * This is actually a wrapper around a for loop and would be used like so:
+ *
+ * fdt_for_each_property_offset(property, fdt, node) {
+ * Use property
+ * ...
+ * }
+ *
+ * if ((property < 0) && (property != -FDT_ERR_NOT_FOUND)) {
+ * Error handling
+ * }
+ *
+ * Note that this is implemented as a macro and property is used as
+ * iterator in the loop. The node variable can be constant or even a
+ * literal.
+ */
+#define fdt_for_each_property_offset(property, fdt, node) \
+ for (property = fdt_first_property_offset(fdt, node); \
+ property >= 0; \
+ property = fdt_next_property_offset(fdt, property))
+
+/**
+ * fdt_get_property_by_offset - retrieve the property at a given offset
+ * @fdt: pointer to the device tree blob
+ * @offset: offset of the property to retrieve
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_property_by_offset() retrieves a pointer to the
+ * fdt_property structure within the device tree blob at the given
+ * offset. If lenp is non-NULL, the length of the property value is
+ * also returned, in the integer pointed to by lenp.
+ *
+ * returns:
+ * pointer to the structure representing the property
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
+ int offset,
+ int *lenp);
+
+/**
+ * fdt_get_property_namelen - find a property based on substring
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @namelen: number of characters of name to consider
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * Identical to fdt_get_property(), but only examine the first namelen
+ * characters of name for matching the property name.
+ */
+#ifndef SWIG /* Not available in Python */
+const struct fdt_property *fdt_get_property_namelen(const void *fdt,
+ int nodeoffset,
+ const char *name,
+ int namelen, int *lenp);
+#endif
+
+/**
+ * fdt_get_property - find a given property in a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_get_property() retrieves a pointer to the fdt_property
+ * structure within the device tree blob corresponding to the property
+ * named 'name' of the node at offset nodeoffset. If lenp is
+ * non-NULL, the length of the property value is also returned, in the
+ * integer pointed to by lenp.
+ *
+ * returns:
+ * pointer to the structure representing the property
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_NOTFOUND, node does not have named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
+ * tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset,
+ const char *name, int *lenp);
+static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset,
+ const char *name,
+ int *lenp)
+{
+ return (struct fdt_property *)(uintptr_t)
+ fdt_get_property(fdt, nodeoffset, name, lenp);
+}
+
+/**
+ * fdt_getprop_by_offset - retrieve the value of a property at a given offset
+ * @fdt: pointer to the device tree blob
+ * @ffset: offset of the property to read
+ * @namep: pointer to a string variable (will be overwritten) or NULL
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_getprop_by_offset() retrieves a pointer to the value of the
+ * property at structure block offset 'offset' (this will be a pointer
+ * to within the device blob itself, not a copy of the value). If
+ * lenp is non-NULL, the length of the property value is also
+ * returned, in the integer pointed to by lenp. If namep is non-NULL,
+ * the property's namne will also be returned in the char * pointed to
+ * by namep (this will be a pointer to within the device tree's string
+ * block, not a new copy of the name).
+ *
+ * returns:
+ * pointer to the property's value
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * if namep is non-NULL *namep contiains a pointer to the property
+ * name.
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#ifndef SWIG /* This function is not useful in Python */
+const void *fdt_getprop_by_offset(const void *fdt, int offset,
+ const char **namep, int *lenp);
+#endif
+
+/**
+ * fdt_getprop_namelen - get property value based on substring
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @namelen: number of characters of name to consider
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * Identical to fdt_getprop(), but only examine the first namelen
+ * characters of name for matching the property name.
+ */
+#ifndef SWIG /* Not available in Python */
+const void *fdt_getprop_namelen(const void *fdt, int nodeoffset,
+ const char *name, int namelen, int *lenp);
+static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset,
+ const char *name, int namelen,
+ int *lenp)
+{
+ return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name,
+ namelen, lenp);
+}
+#endif
+
+/**
+ * fdt_getprop - retrieve the value of a given property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to find
+ * @name: name of the property to find
+ * @lenp: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_getprop() retrieves a pointer to the value of the property
+ * named 'name' of the node at offset nodeoffset (this will be a
+ * pointer to within the device blob itself, not a copy of the value).
+ * If lenp is non-NULL, the length of the property value is also
+ * returned, in the integer pointed to by lenp.
+ *
+ * returns:
+ * pointer to the property's value
+ * if lenp is non-NULL, *lenp contains the length of the property
+ * value (>=0)
+ * NULL, on error
+ * if lenp is non-NULL, *lenp contains an error code (<0):
+ * -FDT_ERR_NOTFOUND, node does not have named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE
+ * tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+const void *fdt_getprop(const void *fdt, int nodeoffset,
+ const char *name, int *lenp);
+static inline void *fdt_getprop_w(void *fdt, int nodeoffset,
+ const char *name, int *lenp)
+{
+ return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp);
+}
+
+/**
+ * fdt_get_phandle - retrieve the phandle of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of the node
+ *
+ * fdt_get_phandle() retrieves the phandle of the device tree node at
+ * structure block offset nodeoffset.
+ *
+ * returns:
+ * the phandle of the node at nodeoffset, on success (!= 0, != -1)
+ * 0, if the node has no phandle, or another error occurs
+ */
+uint32_t fdt_get_phandle(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_get_alias_namelen - get alias based on substring
+ * @fdt: pointer to the device tree blob
+ * @name: name of the alias th look up
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_get_alias(), but only examine the first namelen
+ * characters of name for matching the alias name.
+ */
+#ifndef SWIG /* Not available in Python */
+const char *fdt_get_alias_namelen(const void *fdt,
+ const char *name, int namelen);
+#endif
+
+/**
+ * fdt_get_alias - retrieve the path referenced by a given alias
+ * @fdt: pointer to the device tree blob
+ * @name: name of the alias th look up
+ *
+ * fdt_get_alias() retrieves the value of a given alias. That is, the
+ * value of the property named 'name' in the node /aliases.
+ *
+ * returns:
+ * a pointer to the expansion of the alias named 'name', if it exists
+ * NULL, if the given alias or the /aliases node does not exist
+ */
+const char *fdt_get_alias(const void *fdt, const char *name);
+
+/**
+ * fdt_get_path - determine the full path of a node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose path to find
+ * @buf: character buffer to contain the returned path (will be overwritten)
+ * @buflen: size of the character buffer at buf
+ *
+ * fdt_get_path() computes the full path of the node at offset
+ * nodeoffset, and records that path in the buffer at buf.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ * 0, on success
+ * buf contains the absolute path of the node at
+ * nodeoffset, as a NUL-terminated string.
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1)
+ * characters and will not fit in the given buffer.
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen);
+
+/**
+ * fdt_supernode_atdepth_offset - find a specific ancestor of a node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ * @supernodedepth: depth of the ancestor to find
+ * @nodedepth: pointer to an integer variable (will be overwritten) or NULL
+ *
+ * fdt_supernode_atdepth_offset() finds an ancestor of the given node
+ * at a specific depth from the root (where the root itself has depth
+ * 0, its immediate subnodes depth 1 and so forth). So
+ * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL);
+ * will always return 0, the offset of the root node. If the node at
+ * nodeoffset has depth D, then:
+ * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL);
+ * will return nodeoffset itself.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ * structure block offset of the node at node offset's ancestor
+ * of depth supernodedepth (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of
+ * nodeoffset
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset,
+ int supernodedepth, int *nodedepth);
+
+/**
+ * fdt_node_depth - find the depth of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ *
+ * fdt_node_depth() finds the depth of a given node. The root node
+ * has depth 0, its immediate subnodes depth 1 and so forth.
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset.
+ *
+ * returns:
+ * depth of the node at nodeoffset (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_depth(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_parent_offset - find the parent of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose parent to find
+ *
+ * fdt_parent_offset() locates the parent node of a given node (that
+ * is, it finds the offset of the node which contains the node at
+ * nodeoffset as a subnode).
+ *
+ * NOTE: This function is expensive, as it must scan the device tree
+ * structure from the start to nodeoffset, *twice*.
+ *
+ * returns:
+ * structure block offset of the parent of the node at nodeoffset
+ * (>=0), on success
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_parent_offset(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_node_offset_by_prop_value - find nodes with a given property value
+ * @fdt: pointer to the device tree blob
+ * @startoffset: only find nodes after this offset
+ * @propname: property name to check
+ * @propval: property value to search for
+ * @proplen: length of the value in propval
+ *
+ * fdt_node_offset_by_prop_value() returns the offset of the first
+ * node after startoffset, which has a property named propname whose
+ * value is of length proplen and has value equal to propval; or if
+ * startoffset is -1, the very first such node in the tree.
+ *
+ * To iterate through all nodes matching the criterion, the following
+ * idiom can be used:
+ * offset = fdt_node_offset_by_prop_value(fdt, -1, propname,
+ * propval, proplen);
+ * while (offset != -FDT_ERR_NOTFOUND) {
+ * // other code here
+ * offset = fdt_node_offset_by_prop_value(fdt, offset, propname,
+ * propval, proplen);
+ * }
+ *
+ * Note the -1 in the first call to the function, if 0 is used here
+ * instead, the function will never locate the root node, even if it
+ * matches the criterion.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0, >startoffset),
+ * on success
+ * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
+ * tree after startoffset
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
+ const char *propname,
+ const void *propval, int proplen);
+
+/**
+ * fdt_node_offset_by_phandle - find the node with a given phandle
+ * @fdt: pointer to the device tree blob
+ * @phandle: phandle value
+ *
+ * fdt_node_offset_by_phandle() returns the offset of the node
+ * which has the given phandle value. If there is more than one node
+ * in the tree with the given phandle (an invalid tree), results are
+ * undefined.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0), on success
+ * -FDT_ERR_NOTFOUND, no node with that phandle exists
+ * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1)
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle);
+
+/**
+ * fdt_node_check_compatible: check a node's compatible property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @compatible: string to match against
+ *
+ *
+ * fdt_node_check_compatible() returns 0 if the given node contains a
+ * 'compatible' property with the given string as one of its elements,
+ * it returns non-zero otherwise, or on error.
+ *
+ * returns:
+ * 0, if the node has a 'compatible' property listing the given string
+ * 1, if the node has a 'compatible' property, but it does not list
+ * the given string
+ * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property
+ * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_check_compatible(const void *fdt, int nodeoffset,
+ const char *compatible);
+
+/**
+ * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value
+ * @fdt: pointer to the device tree blob
+ * @startoffset: only find nodes after this offset
+ * @compatible: 'compatible' string to match against
+ *
+ * fdt_node_offset_by_compatible() returns the offset of the first
+ * node after startoffset, which has a 'compatible' property which
+ * lists the given compatible string; or if startoffset is -1, the
+ * very first such node in the tree.
+ *
+ * To iterate through all nodes matching the criterion, the following
+ * idiom can be used:
+ * offset = fdt_node_offset_by_compatible(fdt, -1, compatible);
+ * while (offset != -FDT_ERR_NOTFOUND) {
+ * // other code here
+ * offset = fdt_node_offset_by_compatible(fdt, offset, compatible);
+ * }
+ *
+ * Note the -1 in the first call to the function, if 0 is used here
+ * instead, the function will never locate the root node, even if it
+ * matches the criterion.
+ *
+ * returns:
+ * structure block offset of the located node (>= 0, >startoffset),
+ * on success
+ * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the
+ * tree after startoffset
+ * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE, standard meanings
+ */
+int fdt_node_offset_by_compatible(const void *fdt, int startoffset,
+ const char *compatible);
+
+/**
+ * fdt_stringlist_contains - check a string list property for a string
+ * @strlist: Property containing a list of strings to check
+ * @listlen: Length of property
+ * @str: String to search for
+ *
+ * This is a utility function provided for convenience. The list contains
+ * one or more strings, each terminated by \0, as is found in a device tree
+ * "compatible" property.
+ *
+ * @return: 1 if the string is found in the list, 0 not found, or invalid list
+ */
+int fdt_stringlist_contains(const char *strlist, int listlen, const char *str);
+
+/**
+ * fdt_stringlist_count - count the number of strings in a string list
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @property: name of the property containing the string list
+ * @return:
+ * the number of strings in the given property
+ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
+ * -FDT_ERR_NOTFOUND if the property does not exist
+ */
+int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property);
+
+/**
+ * fdt_stringlist_search - find a string in a string list and return its index
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @property: name of the property containing the string list
+ * @string: string to look up in the string list
+ *
+ * Note that it is possible for this function to succeed on property values
+ * that are not NUL-terminated. That's because the function will stop after
+ * finding the first occurrence of @string. This can for example happen with
+ * small-valued cell properties, such as #address-cells, when searching for
+ * the empty string.
+ *
+ * @return:
+ * the index of the string in the list of strings
+ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
+ * -FDT_ERR_NOTFOUND if the property does not exist or does not contain
+ * the given string
+ */
+int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property,
+ const char *string);
+
+/**
+ * fdt_stringlist_get() - obtain the string at a given index in a string list
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of a tree node
+ * @property: name of the property containing the string list
+ * @index: index of the string to return
+ * @lenp: return location for the string length or an error code on failure
+ *
+ * Note that this will successfully extract strings from properties with
+ * non-NUL-terminated values. For example on small-valued cell properties
+ * this function will return the empty string.
+ *
+ * If non-NULL, the length of the string (on success) or a negative error-code
+ * (on failure) will be stored in the integer pointer to by lenp.
+ *
+ * @return:
+ * A pointer to the string at the given index in the string list or NULL on
+ * failure. On success the length of the string will be stored in the memory
+ * location pointed to by the lenp parameter, if non-NULL. On failure one of
+ * the following negative error codes will be returned in the lenp parameter
+ * (if non-NULL):
+ * -FDT_ERR_BADVALUE if the property value is not NUL-terminated
+ * -FDT_ERR_NOTFOUND if the property does not exist
+ */
+const char *fdt_stringlist_get(const void *fdt, int nodeoffset,
+ const char *property, int index,
+ int *lenp);
+
+/**********************************************************************/
+/* Read-only functions (addressing related) */
+/**********************************************************************/
+
+/**
+ * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells
+ *
+ * This is the maximum value for #address-cells, #size-cells and
+ * similar properties that will be processed by libfdt. IEE1275
+ * requires that OF implementations handle values up to 4.
+ * Implementations may support larger values, but in practice higher
+ * values aren't used.
+ */
+#define FDT_MAX_NCELLS 4
+
+/**
+ * fdt_address_cells - retrieve address size for a bus represented in the tree
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to find the address size for
+ *
+ * When the node has a valid #address-cells property, returns its value.
+ *
+ * returns:
+ * 0 <= n < FDT_MAX_NCELLS, on success
+ * 2, if the node has no #address-cells property
+ * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
+ * #address-cells property
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_address_cells(const void *fdt, int nodeoffset);
+
+/**
+ * fdt_size_cells - retrieve address range size for a bus represented in the
+ * tree
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to find the address range size for
+ *
+ * When the node has a valid #size-cells property, returns its value.
+ *
+ * returns:
+ * 0 <= n < FDT_MAX_NCELLS, on success
+ * 2, if the node has no #address-cells property
+ * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid
+ * #size-cells property
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_size_cells(const void *fdt, int nodeoffset);
+
+
+/**********************************************************************/
+/* Write-in-place functions */
+/**********************************************************************/
+
+/**
+ * fdt_setprop_inplace_namelen_partial - change a property's value,
+ * but not its size
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @namelen: number of characters of name to consider
+ * @idx: index of the property to change in the array
+ * @val: pointer to data to replace the property value with
+ * @len: length of the property value
+ *
+ * Identical to fdt_setprop_inplace(), but modifies the given property
+ * starting from the given index, and using only the first characters
+ * of the name. It is useful when you want to manipulate only one value of
+ * an array and you have a string that doesn't end with \0.
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset,
+ const char *name, int namelen,
+ uint32_t idx, const void *val,
+ int len);
+#endif
+
+/**
+ * fdt_setprop_inplace - change a property's value, but not its size
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: pointer to data to replace the property value with
+ * @len: length of the property value
+ *
+ * fdt_setprop_inplace() replaces the value of a given property with
+ * the data in val, of length len. This function cannot change the
+ * size of a property, and so will only work if len is equal to the
+ * current length of the property.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if len is not equal to the property's current length
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+#endif
+
+/**
+ * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 32-bit integer value to replace the property with
+ *
+ * fdt_setprop_inplace_u32() replaces the value of a given property
+ * with the 32-bit integer value in val, converting val to big-endian
+ * if necessary. This function cannot change the size of a property,
+ * and so will only work if the property already exists and has length
+ * 4.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if the property's length is not equal to 4
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ fdt32_t tmp = cpu_to_fdt32(val);
+ return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 64-bit integer value to replace the property with
+ *
+ * fdt_setprop_inplace_u64() replaces the value of a given property
+ * with the 64-bit integer value in val, converting val to big-endian
+ * if necessary. This function cannot change the size of a property,
+ * and so will only work if the property already exists and has length
+ * 8.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the given property value, and will not alter or move any other part
+ * of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, if the property's length is not equal to 8
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset,
+ const char *name, uint64_t val)
+{
+ fdt64_t tmp = cpu_to_fdt64(val);
+ return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_inplace_cell - change the value of a single-cell property
+ *
+ * This is an alternative name for fdt_setprop_inplace_u32()
+ */
+static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val);
+}
+
+/**
+ * fdt_nop_property - replace a property with nop tags
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to nop
+ * @name: name of the property to nop
+ *
+ * fdt_nop_property() will replace a given property's representation
+ * in the blob with FDT_NOP tags, effectively removing it from the
+ * tree.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the property, and will not alter or move any other part of the
+ * tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_nop_property(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_nop_node - replace a node (subtree) with nop tags
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to nop
+ *
+ * fdt_nop_node() will replace a given node's representation in the
+ * blob, including all its subnodes, if any, with FDT_NOP tags,
+ * effectively removing it from the tree.
+ *
+ * This function will alter only the bytes in the blob which contain
+ * the node and its properties and subnodes, and will not alter or
+ * move any other part of the tree.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_nop_node(void *fdt, int nodeoffset);
+
+/**********************************************************************/
+/* Sequential write functions */
+/**********************************************************************/
+
+int fdt_create(void *buf, int bufsize);
+int fdt_resize(void *fdt, void *buf, int bufsize);
+int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size);
+int fdt_finish_reservemap(void *fdt);
+int fdt_begin_node(void *fdt, const char *name);
+int fdt_property(void *fdt, const char *name, const void *val, int len);
+static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val)
+{
+ fdt32_t tmp = cpu_to_fdt32(val);
+ return fdt_property(fdt, name, &tmp, sizeof(tmp));
+}
+static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val)
+{
+ fdt64_t tmp = cpu_to_fdt64(val);
+ return fdt_property(fdt, name, &tmp, sizeof(tmp));
+}
+static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val)
+{
+ return fdt_property_u32(fdt, name, val);
+}
+#define fdt_property_string(fdt, name, str) \
+ fdt_property(fdt, name, str, strlen(str)+1)
+int fdt_end_node(void *fdt);
+int fdt_finish(void *fdt);
+
+/**********************************************************************/
+/* Read-write functions */
+/**********************************************************************/
+
+int fdt_create_empty_tree(void *buf, int bufsize);
+int fdt_open_into(const void *fdt, void *buf, int bufsize);
+int fdt_pack(void *fdt);
+
+/**
+ * fdt_add_mem_rsv - add one memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @address, @size: 64-bit values (native endian)
+ *
+ * Adds a reserve map entry to the given blob reserving a region at
+ * address address of length size.
+ *
+ * This function will insert data into the reserve map and will
+ * therefore change the indexes of some entries in the table.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new reservation entry
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size);
+
+/**
+ * fdt_del_mem_rsv - remove a memory reserve map entry
+ * @fdt: pointer to the device tree blob
+ * @n: entry to remove
+ *
+ * fdt_del_mem_rsv() removes the n-th memory reserve map entry from
+ * the blob.
+ *
+ * This function will delete data from the reservation table and will
+ * therefore change the indexes of some entries in the table.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there
+ * are less than n+1 reserve map entries)
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_del_mem_rsv(void *fdt, int n);
+
+/**
+ * fdt_set_name - change the name of a given node
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: structure block offset of a node
+ * @name: name to give the node
+ *
+ * fdt_set_name() replaces the name (including unit address, if any)
+ * of the given node with the given string. NOTE: this function can't
+ * efficiently check if the new name is unique amongst the given
+ * node's siblings; results are undefined if this function is invoked
+ * with a name equal to one of the given node's siblings.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob
+ * to contain the new name
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE, standard meanings
+ */
+int fdt_set_name(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_setprop - create or change a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: pointer to data to set the property value to
+ * @len: length of the property value
+ *
+ * fdt_setprop() sets the value of the named property in the given
+ * node to the given value and length, creating the property if it
+ * does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_setprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+
+/**
+ * fdt_setprop_u32 - set a property to a 32-bit integer
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 32-bit integer value for the property (native endian)
+ *
+ * fdt_setprop_u32() sets the value of the named property in the given
+ * node to the given 32-bit integer value (converting to big-endian if
+ * necessary), or creates a new property with that value if it does
+ * not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name,
+ uint32_t val)
+{
+ fdt32_t tmp = cpu_to_fdt32(val);
+ return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_u64 - set a property to a 64-bit integer
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 64-bit integer value for the property (native endian)
+ *
+ * fdt_setprop_u64() sets the value of the named property in the given
+ * node to the given 64-bit integer value (converting to big-endian if
+ * necessary), or creates a new property with that value if it does
+ * not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name,
+ uint64_t val)
+{
+ fdt64_t tmp = cpu_to_fdt64(val);
+ return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_setprop_cell - set a property to a single cell value
+ *
+ * This is an alternative name for fdt_setprop_u32()
+ */
+static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name,
+ uint32_t val)
+{
+ return fdt_setprop_u32(fdt, nodeoffset, name, val);
+}
+
+/**
+ * fdt_setprop_string - set a property to a string value
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @str: string value for the property
+ *
+ * fdt_setprop_string() sets the value of the named property in the
+ * given node to the given string value (using the length of the
+ * string to determine the new length of the property), or creates a
+ * new property with that value if it does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_setprop_string(fdt, nodeoffset, name, str) \
+ fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
+
+
+/**
+ * fdt_setprop_empty - set a property to an empty value
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ *
+ * fdt_setprop_empty() sets the value of the named property in the
+ * given node to an empty (zero length) value, or creates a new empty
+ * property if it does not already exist.
+ *
+ * This function may insert or delete data from the blob, and will
+ * therefore change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_setprop_empty(fdt, nodeoffset, name) \
+ fdt_setprop((fdt), (nodeoffset), (name), NULL, 0)
+
+/**
+ * fdt_appendprop - append to or create a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to append to
+ * @val: pointer to data to append to the property value
+ * @len: length of the data to append to the property value
+ *
+ * fdt_appendprop() appends the value to the named property in the
+ * given node, creating the property if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_appendprop(void *fdt, int nodeoffset, const char *name,
+ const void *val, int len);
+
+/**
+ * fdt_appendprop_u32 - append a 32-bit integer value to a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 32-bit integer value to append to the property (native endian)
+ *
+ * fdt_appendprop_u32() appends the given 32-bit integer value
+ * (converting to big-endian if necessary) to the value of the named
+ * property in the given node, or creates a new property with that
+ * value if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_appendprop_u32(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ fdt32_t tmp = cpu_to_fdt32(val);
+ return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_appendprop_u64 - append a 64-bit integer value to a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @val: 64-bit integer value to append to the property (native endian)
+ *
+ * fdt_appendprop_u64() appends the given 64-bit integer value
+ * (converting to big-endian if necessary) to the value of the named
+ * property in the given node, or creates a new property with that
+ * value if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+static inline int fdt_appendprop_u64(void *fdt, int nodeoffset,
+ const char *name, uint64_t val)
+{
+ fdt64_t tmp = cpu_to_fdt64(val);
+ return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp));
+}
+
+/**
+ * fdt_appendprop_cell - append a single cell value to a property
+ *
+ * This is an alternative name for fdt_appendprop_u32()
+ */
+static inline int fdt_appendprop_cell(void *fdt, int nodeoffset,
+ const char *name, uint32_t val)
+{
+ return fdt_appendprop_u32(fdt, nodeoffset, name, val);
+}
+
+/**
+ * fdt_appendprop_string - append a string to a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to change
+ * @name: name of the property to change
+ * @str: string value to append to the property
+ *
+ * fdt_appendprop_string() appends the given string to the value of
+ * the named property in the given node, or creates a new property
+ * with that value if it does not already exist.
+ *
+ * This function may insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to
+ * contain the new property value
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+#define fdt_appendprop_string(fdt, nodeoffset, name, str) \
+ fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1)
+
+/**
+ * fdt_delprop - delete a property
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node whose property to nop
+ * @name: name of the property to nop
+ *
+ * fdt_del_property() will delete the given property.
+ *
+ * This function will delete data from the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOTFOUND, node does not have the named property
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_delprop(void *fdt, int nodeoffset, const char *name);
+
+/**
+ * fdt_add_subnode_namelen - creates a new node based on substring
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ * @namelen: number of characters of name to consider
+ *
+ * Identical to fdt_add_subnode(), but use only the first namelen
+ * characters of name as the name of the new node. This is useful for
+ * creating subnodes based on a portion of a larger string, such as a
+ * full path.
+ */
+#ifndef SWIG /* Not available in Python */
+int fdt_add_subnode_namelen(void *fdt, int parentoffset,
+ const char *name, int namelen);
+#endif
+
+/**
+ * fdt_add_subnode - creates a new node
+ * @fdt: pointer to the device tree blob
+ * @parentoffset: structure block offset of a node
+ * @name: name of the subnode to locate
+ *
+ * fdt_add_subnode() creates a new node as a subnode of the node at
+ * structure block offset parentoffset, with the given name (which
+ * should include the unit address, if any).
+ *
+ * This function will insert data into the blob, and will therefore
+ * change the offsets of some existing nodes.
+
+ * returns:
+ * structure block offset of the created nodeequested subnode (>=0), on
+ * success
+ * -FDT_ERR_NOTFOUND, if the requested subnode does not exist
+ * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE
+ * tag
+ * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of
+ * the given name
+ * -FDT_ERR_NOSPACE, if there is insufficient free space in the
+ * blob to contain the new node
+ * -FDT_ERR_NOSPACE
+ * -FDT_ERR_BADLAYOUT
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings.
+ */
+int fdt_add_subnode(void *fdt, int parentoffset, const char *name);
+
+/**
+ * fdt_del_node - delete a node (subtree)
+ * @fdt: pointer to the device tree blob
+ * @nodeoffset: offset of the node to nop
+ *
+ * fdt_del_node() will remove the given node, including all its
+ * subnodes if any, from the blob.
+ *
+ * This function will delete data from the blob, and will therefore
+ * change the offsets of some existing nodes.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_del_node(void *fdt, int nodeoffset);
+
+/**
+ * fdt_overlay_apply - Applies a DT overlay on a base DT
+ * @fdt: pointer to the base device tree blob
+ * @fdto: pointer to the device tree overlay blob
+ *
+ * fdt_overlay_apply() will apply the given device tree overlay on the
+ * given base device tree.
+ *
+ * Expect the base device tree to be modified, even if the function
+ * returns an error.
+ *
+ * returns:
+ * 0, on success
+ * -FDT_ERR_NOSPACE, there's not enough space in the base device tree
+ * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or
+ * properties in the base DT
+ * -FDT_ERR_BADPHANDLE,
+ * -FDT_ERR_BADOVERLAY,
+ * -FDT_ERR_NOPHANDLES,
+ * -FDT_ERR_INTERNAL,
+ * -FDT_ERR_BADLAYOUT,
+ * -FDT_ERR_BADMAGIC,
+ * -FDT_ERR_BADOFFSET,
+ * -FDT_ERR_BADPATH,
+ * -FDT_ERR_BADVERSION,
+ * -FDT_ERR_BADSTRUCTURE,
+ * -FDT_ERR_BADSTATE,
+ * -FDT_ERR_TRUNCATED, standard meanings
+ */
+int fdt_overlay_apply(void *fdt, void *fdto);
+
+/**********************************************************************/
+/* Debugging / informational functions */
+/**********************************************************************/
+
+const char *fdt_strerror(int errval);
+
+#endif /* _LIBFDT_H */
diff --git a/components/drivers/fdt/libfdt/libfdt_env.h b/components/drivers/fdt/libfdt/libfdt_env.h
new file mode 100644
index 0000000000..f4a6b6063b
--- /dev/null
+++ b/components/drivers/fdt/libfdt/libfdt_env.h
@@ -0,0 +1,113 @@
+#ifndef _LIBFDT_ENV_H
+#define _LIBFDT_ENV_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ * Copyright 2012 Kim Phillips, Freescale Semiconductor.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __CHECKER__
+#define FDT_FORCE __attribute__((force))
+#define FDT_BITWISE __attribute__((bitwise))
+#else
+#define FDT_FORCE
+#define FDT_BITWISE
+#endif
+
+typedef uint16_t FDT_BITWISE fdt16_t;
+typedef uint32_t FDT_BITWISE fdt32_t;
+typedef uint64_t FDT_BITWISE fdt64_t;
+typedef uint64_t unaligned_fdt64_t __attribute__((aligned(4)));
+
+#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n])
+#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1))
+#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \
+ (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3))
+#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \
+ (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \
+ (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \
+ (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7))
+
+static inline uint16_t fdt16_to_cpu(fdt16_t x)
+{
+ return (FDT_FORCE uint16_t)CPU_TO_FDT16(x);
+}
+static inline fdt16_t cpu_to_fdt16(uint16_t x)
+{
+ return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x);
+}
+
+static inline uint32_t fdt32_to_cpu(fdt32_t x)
+{
+ return (FDT_FORCE uint32_t)CPU_TO_FDT32(x);
+}
+static inline fdt32_t cpu_to_fdt32(uint32_t x)
+{
+ return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x);
+}
+
+static inline uint64_t fdt64_to_cpu(fdt64_t x)
+{
+ return (FDT_FORCE uint64_t)CPU_TO_FDT64(x);
+}
+static inline fdt64_t cpu_to_fdt64(uint64_t x)
+{
+ return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x);
+}
+#undef CPU_TO_FDT64
+#undef CPU_TO_FDT32
+#undef CPU_TO_FDT16
+#undef EXTRACT_BYTE
+
+#endif /* _LIBFDT_ENV_H */
diff --git a/components/drivers/fdt/libfdt/libfdt_internal.h b/components/drivers/fdt/libfdt/libfdt_internal.h
new file mode 100644
index 0000000000..5cb817b365
--- /dev/null
+++ b/components/drivers/fdt/libfdt/libfdt_internal.h
@@ -0,0 +1,95 @@
+#ifndef _LIBFDT_INTERNAL_H
+#define _LIBFDT_INTERNAL_H
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Copyright (C) 2006 David Gibson, IBM Corporation.
+ *
+ * libfdt is dual licensed: you can use it either under the terms of
+ * the GPL, or the BSD license, at your option.
+ *
+ * a) This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ *
+ * Alternatively,
+ *
+ * b) Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "fdt.h"
+
+#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1))
+#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE))
+
+#define FDT_CHECK_HEADER(fdt) \
+ { \
+ int __err; \
+ if ((__err = fdt_check_header(fdt)) != 0) \
+ return __err; \
+ }
+
+int _fdt_check_node_offset(const void *fdt, int offset);
+int _fdt_check_prop_offset(const void *fdt, int offset);
+const char *_fdt_find_string(const char *strtab, int tabsize, const char *s);
+int _fdt_node_end_offset(void *fdt, int nodeoffset);
+
+static inline const void *_fdt_offset_ptr(const void *fdt, int offset)
+{
+ return (const char *)fdt + fdt_off_dt_struct(fdt) + offset;
+}
+
+static inline void *_fdt_offset_ptr_w(void *fdt, int offset)
+{
+ return (void *)(uintptr_t)_fdt_offset_ptr(fdt, offset);
+}
+
+static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n)
+{
+ const struct fdt_reserve_entry *rsv_table =
+ (const struct fdt_reserve_entry *)
+ ((const char *)fdt + fdt_off_mem_rsvmap(fdt));
+
+ return rsv_table + n;
+}
+static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n)
+{
+ return (void *)(uintptr_t)_fdt_mem_rsv(fdt, n);
+}
+
+#define FDT_SW_MAGIC (~FDT_MAGIC)
+
+#endif /* _LIBFDT_INTERNAL_H */
diff --git a/components/drivers/fdt/src/SConscript b/components/drivers/fdt/src/SConscript
new file mode 100644
index 0000000000..74ae118929
--- /dev/null
+++ b/components/drivers/fdt/src/SConscript
@@ -0,0 +1,17 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+list = os.listdir(cwd)
+CPPPATH = [cwd + '/../inc' , cwd + '/libfdt']
+objs = []
+
+group = DefineGroup('FDT', src, depend = ['RT_USING_FDT'], CPPPATH = CPPPATH)
+
+for d in list:
+ path = os.path.join(cwd, d)
+ if os.path.isfile(os.path.join(path, 'SConscript')):
+ objs = objs + SConscript(os.path.join(d, 'SConscript'))
+objs = objs + group
+
+Return('objs') \ No newline at end of file
diff --git a/components/drivers/fdt/src/dtb_access.c b/components/drivers/fdt/src/dtb_access.c
new file mode 100644
index 0000000000..71de2989db
--- /dev/null
+++ b/components/drivers/fdt/src/dtb_access.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define _GNU_SOURCE
+
+#include "libfdt.h"
+#include "dtb_node.h"
+
+/* "/aliaes" node */
+static struct dtb_node *fdt_aliases;
+
+/**
+ * of_find_property_value_of_size() - find property of given size
+ *
+ * Search for a property in a device node and validate the requested size.
+ *
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @len: requested length of property value
+ *
+ * @return the property value on success, -EINVAL if the property does not
+ * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ */
+static void *dtb_node_find_property_value_of_size(const struct dtb_node *dn,
+ const char *propname, uint32_t len)
+{
+ struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL);
+
+ if (!prop)
+ return ERR_PTR(-EINVAL);
+ if (!prop->value)
+ return ERR_PTR(-ENODATA);
+ if (len > prop->size)
+ return ERR_PTR(-EOVERFLOW);
+ return prop->value;
+}
+
+int dtb_node_read_u32(const struct dtb_node *dn, const char *propname, uint32_t *outp)
+{
+ const uint32_t *val;
+
+ debug("%s: %s: \n", __func__, propname);
+ if (!dn)
+ return -EINVAL;
+ val = dtb_node_find_property_value_of_size(dn, propname, sizeof(*outp));
+ if (IS_ERR(val))
+ {
+ debug("(not found)\n");
+ return PTR_ERR(val);
+ }
+
+ *outp = fdt32_to_cpu(*val);
+ debug("%#x (%d)\n", *outp, *outp);
+
+ return 0;
+}
+
+uint32_t dtb_node_read_u32_default(const struct dtb_node *node, const char *propname, uint32_t def)
+{
+ dtb_node_read_u32(node, propname, &def);
+
+ return def;
+}
+
+int dtb_node_read_u32_array(const struct dtb_node *dn, const char *propname,
+ uint32_t *out_values, size_t sz)
+{
+ const uint32_t *val;
+
+ debug("%s: %s: ", __func__, propname);
+ val = dtb_node_find_property_value_of_size(dn, propname,
+ sz * sizeof(*out_values));
+
+ if (IS_ERR(val))
+ return PTR_ERR(val);
+
+ debug("size %zd, val:%d\n", sz, *val);
+ while (sz--)
+ *out_values++ = fdt32_to_cpu(*val++);
+
+ return 0;
+}
+
+uint32_t dtb_node_read_u32_index_default(const struct dtb_node *node, const char *propname, int index,
+ uint32_t def)
+{
+ RT_ASSERT(dtb_node_valid(node));
+ dtb_node_read_u32_index(node, propname, index, &def);
+
+ return def;
+}
+
+int dtb_node_read_s32_default(const struct dtb_node *node, const char *propname, int32_t def)
+{
+ RT_ASSERT(dtb_node_valid(node));
+ dtb_node_read_u32(node, propname, (uint32_t *)&def);
+
+ return def;
+}
+
+int dtb_node_read_u32_index(const struct dtb_node *dn, const char *propname,
+ int index, uint32_t *outp)
+{
+ const uint32_t *val;
+
+ debug("%s: %s: ", __func__, propname);
+ if (!dn)
+ return -EINVAL;
+
+ val = dtb_node_find_property_value_of_size(dn, propname,
+ sizeof(*outp) * (index + 1));
+ if (IS_ERR(val))
+ {
+ debug("(not found)\n");
+ return PTR_ERR(val);
+ }
+
+ *outp = fdt32_to_cpu(val[index]);
+ debug("%#x (%d)\n", *outp, *outp);
+
+ return 0;
+}
+
+int dtb_node_read_u64(const struct dtb_node *dn, const char *propname, uint64_t *outp)
+{
+ const uint64_t *val;
+
+ debug("%s: %s: ", __func__, propname);
+ if (!dn)
+ return -EINVAL;
+ val = dtb_node_find_property_value_of_size(dn, propname, sizeof(*outp));
+ if (IS_ERR(val))
+ {
+ debug("(not found)\n");
+ return PTR_ERR(val);
+ }
+
+ *outp = fdt64_to_cpu(*val);
+ debug("%#llx (%lld)\n", (unsigned long long)*outp,
+ (unsigned long long)*outp);
+
+ return 0;
+}
+
+uint64_t dtb_node_read_u64_default(const struct dtb_node *node, const char *propname, uint64_t def)
+{
+ RT_ASSERT(dtb_node_valid(node));
+ dtb_node_read_u64(node, propname, &def);
+
+ return def;
+}
+
+int dtb_node_n_addr_cells(const struct dtb_node *dn)
+{
+ const uint32_t *ip;
+
+ do
+ {
+ if (dn->parent)
+ dn = dn->parent;
+ ip = dtb_node_get_dtb_node_property_value(dn, "#address-cells", NULL);
+ if (ip)
+ return fdt32_to_cpu(*ip);
+ } while (dn->parent);
+
+ /* No #address-cells property for the root node */
+ return DEV_ROOT_NODE_ADDR_CELLS_DEFAULT;
+}
+
+int dtb_node_n_size_cells(const struct dtb_node *dn)
+{
+ const uint32_t *ip;
+
+ do
+ {
+ if (dn->parent)
+ dn = dn->parent;
+ ip = dtb_node_get_dtb_node_property_value(dn, "#size-cells", NULL);
+ if (ip)
+ return fdt32_to_cpu(*ip);
+ } while (dn->parent);
+
+ /* No #size-cells property for the root node */
+ return DEV_ROOT_NODE_SIZE_CELLS_DEFAULT;
+}
+
+int dtb_node_simple_addr_cells(const struct dtb_node *dn)
+{
+ const uint32_t *ip;
+
+ ip = dtb_node_get_dtb_node_property_value(dn, "#address-cells", NULL);
+ if (ip)
+ return fdt32_to_cpu(*ip);
+
+ /* Return a default of 2 to match fdt_address_cells()*/
+ return 2;
+}
+
+int dtb_node_simple_size_cells(const struct dtb_node *dn)
+{
+ const uint32_t *ip;
+
+ ip = dtb_node_get_dtb_node_property_value(dn, "#size-cells", NULL);
+ if (ip)
+ return fdt32_to_cpu(*ip);
+
+ /* Return a default of 2 to match fdt_size_cells()*/
+ return 2;
+}
+
+struct dtb_property *dtb_node_get_dtb_node_property(const struct dtb_node *dtb_node, const char *property_name, int *property_size)
+{
+ struct dtb_property *dtb_property = NULL;
+
+ if (dtb_node != NULL && property_name != NULL)
+ {
+ dtb_property = dtb_node->properties;
+
+ while (dtb_property != NULL)
+ {
+ if (!strcmp(dtb_property->name, property_name))
+ {
+ if (property_size != NULL)
+ {
+ *property_size = dtb_property->size;
+ }
+ return dtb_property;
+ }
+ dtb_property = dtb_property->next;
+ }
+ }
+
+ return dtb_property;
+}
+
+#define for_each_property_of_node(dn, pp) \
+ for (pp = dn->properties; pp != NULL; pp = pp->next)
+
+struct dtb_node *dtb_node_find_node_opts_by_path(const char *path,
+ const char **opts)
+{
+ struct dtb_node *np = NULL;
+ struct dtb_property *pp;
+ const char *separator = strchr(path, ':');
+
+ if (opts)
+ *opts = separator ? separator + 1 : NULL;
+
+ if (strcmp(path, "/") == 0)
+ return dtb_node_get(get_dtb_node_head());
+
+ /* The path could begin with an alias */
+ if (*path != '/')
+ {
+ int len;
+ const char *p = separator;
+
+ if (!p)
+ p = strchrnul(path, '/');
+ len = p - path;
+
+ /* of_aliases must not be NULL */
+ if (!fdt_aliases)
+ return NULL;
+
+ for_each_property_of_node(fdt_aliases, pp)
+ {
+ if (strlen(pp->name) == len && !strncmp(pp->name, path,
+ len))
+ {
+ np = dtb_node_find_node_by_path(pp->value);
+ break;
+ }
+ }
+ if (!np)
+ return NULL;
+ path = p;
+ }
+
+ /* Step down the tree matching path components */
+ if (!np)
+ np = dtb_node_get(get_dtb_node_head());
+ while (np && *path == '/')
+ {
+ struct dtb_node *tmp = np;
+
+ path++; /* Increment past '/' delimiter */
+ np = dtb_node_get_dtb_node_by_path(np, path);
+ dtb_node_put(tmp);
+ path = strchrnul(path, '/');
+ if (separator && separator < path)
+ break;
+ }
+
+ return np;
+}
+
+struct dtb_node *dtb_node_find_compatible_node(struct dtb_node *from, const char *compatible)
+{
+ struct dtb_node *dn;
+
+ for_each_of_allnodes_from(from, dn)
+ {
+ if (dtb_node_get_dtb_node_compatible_match(dn, compatible) &&
+ dtb_node_get(dn))
+ break;
+ }
+ dtb_node_put(from);
+
+ return dn;
+}
+
+void *dtb_node_get_dtb_node_property_value(const struct dtb_node *dtb_node, const char *property_name, int *property_size)
+{
+ struct dtb_property *dtb_property = dtb_node_get_dtb_node_property(dtb_node, property_name, NULL);
+
+ if (!dtb_property || !dtb_property->value || !dtb_property->size)
+ {
+ return NULL;
+ }
+
+ if (property_size != NULL)
+ {
+ *property_size = dtb_property->size;
+ }
+
+ return dtb_property->value;
+}
+
+const struct dtb_node *dtb_node_find_node_by_prop_value(struct dtb_node *from,
+ const char *propname,
+ const void *propval, int proplen)
+{
+ struct dtb_node *np;
+ void *value;
+ for_each_of_allnodes_from(from, np)
+ {
+ value = dtb_node_get_dtb_node_property_value(np, propname, &proplen);
+ if (!memcmp(value, propval, proplen) && dtb_node_get(np))
+ break;
+ }
+ dtb_node_put(from);
+
+ return np;
+}
+
+struct dtb_node *dtb_node_find_all_nodes(const struct dtb_node *prev)
+{
+ const struct dtb_node *dn;
+
+ if (!prev)
+ {
+ dn = get_dtb_node_head();
+ }
+ else if (prev->child)
+ {
+ dn = prev->child;
+ }
+ else
+ {
+ /*
+ * Walk back up looking for a sibling, or the end of the
+ * structure
+ */
+ dn = prev;
+ while (dn->parent && !dn->sibling)
+ dn = dn->parent;
+ dn = dn->sibling; /* Might be null at the end of the tree */
+ }
+
+ return (struct dtb_node *)dn;
+}
+
+rt_bool_t dtb_node_device_is_available(const struct dtb_node *device)
+{
+ const char *status;
+ int statlen;
+
+ if (!device)
+ return RT_FALSE;
+
+ status = dtb_node_get_dtb_node_property_value(device, "status", &statlen);
+ if (status == NULL)
+ return RT_TRUE;
+
+ if (statlen > 0)
+ {
+ if (!strcmp(status, "okay"))
+ return RT_TRUE;
+ }
+
+ return RT_FALSE;
+}
+
+struct dtb_node *dtb_node_get_parent(const struct dtb_node *node)
+{
+ const struct dtb_node *dn;
+
+ if (!node)
+ return NULL;
+
+ dn = dtb_node_get(node->parent);
+
+ return (struct dtb_node *)dn;
+}
+
+struct dtb_node *dtb_node_find_node_by_phandle(phandle handle)
+{
+ struct dtb_node *dn;
+
+ if (!handle)
+ return NULL;
+
+ for_each_of_allnodes(dn) if (dn->handle == handle) break;
+ (void)dtb_node_get(dn);
+
+ return dn;
+}
+
+int dtb_node_property_match_string(const struct dtb_node *dn, const char *propname,
+ const char *string)
+{
+ const struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL);
+ size_t l;
+ int i;
+ const char *p, *end;
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ p = prop->value;
+ end = p + prop->size;
+
+ for (i = 0; p < end; i++, p += l)
+ {
+ l = strnlen(p, end - p) + 1;
+ if (p + l > end)
+ return -EILSEQ;
+ debug("comparing %s with %s\n", string, p);
+ if (strcmp(string, p) == 0)
+ return i; /* Found it; return index */
+ }
+ return -ENODATA;
+}
+
+/**
+ * of_property_read_string_helper() - Utility helper for parsing string properties
+ * @np: device node from which the property value is to be read.
+ * @propname: name of the property to be searched.
+ * @out_strs: output array of string pointers.
+ * @sz: number of array elements to read.
+ * @skip: Number of strings to skip over at beginning of list.
+ *
+ * Don't call this function directly. It is a utility helper for the
+ * of_property_read_string*() family of functions.
+ */
+int dtb_node_property_read_string_helper(const struct dtb_node *dn,
+ const char *propname, const char **out_strs,
+ size_t sz, int skip)
+{
+ const struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL);
+ int l = 0, i = 0;
+ const char *p, *end;
+
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+ p = prop->value;
+ end = p + prop->size;
+
+ for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l)
+ {
+ l = strnlen(p, end - p) + 1;
+ if (p + l > end)
+ return -EILSEQ;
+ if (out_strs && i >= skip)
+ *out_strs++ = p;
+ }
+ i -= skip;
+ return i <= 0 ? -ENODATA : i;
+}
+
+static int __dtb_node_parse_phandle_with_args(const struct dtb_node *dn,
+ const char *list_name,
+ const char *cells_name,
+ int cell_count, int index,
+ struct fdt_phandle_args *out_args)
+{
+ const uint32_t *list, *list_end;
+ int rc = 0, cur_index = 0;
+ uint32_t count = 0;
+ struct dtb_node *node = NULL;
+ phandle phandle;
+ int size;
+
+ /* Retrieve the phandle list property */
+ list = dtb_node_get_dtb_node_property_value(dn, list_name, &size);
+ if (!list)
+ return -ENOENT;
+ list_end = list + size / sizeof(*list);
+
+ /* Loop over the phandles until all the requested entry is found */
+ while (list < list_end)
+ {
+ rc = -EINVAL;
+ count = 0;
+
+ /*
+ * If phandle is 0, then it is an empty entry with no
+ * arguments. Skip forward to the next entry.
+ */
+ phandle = fdt32_to_cpu(*(list++));
+ if (phandle)
+ {
+ /*
+ * Find the provider node and parse the #*-cells
+ * property to determine the argument length.
+ *
+ * This is not needed if the cell count is hard-coded
+ * (i.e. cells_name not set, but cell_count is set),
+ * except when we're going to return the found node
+ * below.
+ */
+ if (cells_name || cur_index == index)
+ {
+ node = dtb_node_find_node_by_phandle(phandle);
+ if (!node)
+ {
+ debug("%s: could not find phandle\n",
+ dn->path);
+ goto err;
+ }
+ }
+
+ if (cells_name)
+ {
+ if (dtb_node_read_u32(node, cells_name, &count))
+ {
+ debug("%s: could not get %s for %s\n",
+ dn->path, cells_name,
+ node->path);
+ goto err;
+ }
+ }
+ else
+ {
+ count = cell_count;
+ }
+
+ /*
+ * Make sure that the arguments actually fit in the
+ * remaining property data length
+ */
+ if (list + count > list_end)
+ {
+ debug("%s: arguments longer than property\n",
+ dn->path);
+ goto err;
+ }
+ }
+
+ /*
+ * All of the error cases above bail out of the loop, so at
+ * this point, the parsing is successful. If the requested
+ * index matches, then fill the out_args structure and return,
+ * or return -ENOENT for an empty entry.
+ */
+ rc = -ENOENT;
+ if (cur_index == index)
+ {
+ if (!phandle)
+ goto err;
+
+ if (out_args)
+ {
+ int i;
+ if (count > FDT_MAX_PHANDLE_ARGS)
+ count = FDT_MAX_PHANDLE_ARGS;
+ out_args->np = node;
+ out_args->args_count = count;
+ for (i = 0; i < count; i++)
+ out_args->args[i] =
+ fdt32_to_cpu(*(list++));
+ }
+ else
+ {
+ dtb_node_put(node);
+ }
+
+ /* Found it! return success */
+ return 0;
+ }
+
+ dtb_node_put(node);
+ node = NULL;
+ list += count;
+ cur_index++;
+ }
+
+ /*
+ * Unlock node before returning result; will be one of:
+ * -ENOENT : index is for empty phandle
+ * -EINVAL : parsing error on data
+ * [1..n] : Number of phandle (count mode; when index = -1)
+ */
+ rc = index < 0 ? cur_index : -ENOENT;
+err:
+ if (node)
+ dtb_node_put(node);
+ return rc;
+}
+
+struct dtb_node *dtb_node_parse_phandle(const struct dtb_node *dn,
+ const char *phandle_name, int index)
+{
+ struct fdt_phandle_args args;
+
+ if (index < 0)
+ return NULL;
+
+ if (__dtb_node_parse_phandle_with_args(dn, phandle_name, NULL, 0, index,
+ &args))
+ return NULL;
+
+ return args.np;
+}
+
+int dtb_node_parse_phandle_with_args(const struct dtb_node *dn,
+ const char *list_name, const char *cells_name,
+ int index, struct fdt_phandle_args *out_args)
+{
+ if (index < 0)
+ return -EINVAL;
+
+ return __dtb_node_parse_phandle_with_args(dn, list_name, cells_name, 0,
+ index, out_args);
+}
+
+int dtb_node_count_phandle_with_args(const struct dtb_node *dn,
+ const char *list_name, const char *cells_name)
+{
+ return __dtb_node_parse_phandle_with_args(dn, list_name, cells_name, 0,
+ -1, NULL);
+}
diff --git a/components/drivers/fdt/src/dtb_addr.c b/components/drivers/fdt/src/dtb_addr.c
new file mode 100644
index 0000000000..e4a296ea7f
--- /dev/null
+++ b/components/drivers/fdt/src/dtb_addr.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "libfdt.h"
+#include "dtb_node.h"
+
+/* Max address size we deal with */
+#define FDT_MAX_ADDR_CELLS 4
+#define FDT_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= FDT_MAX_ADDR_CELLS)
+#define FDT_CHECK_COUNTS(na, ns) (FDT_CHECK_ADDR_COUNT(na) && (ns) > 0)
+
+static void dtb_node_default_count_cells(const struct dtb_node *dn,
+ int *addrc, int *sizec)
+{
+ if (addrc)
+ *addrc = dtb_node_n_addr_cells(dn);
+ if (sizec)
+ *sizec = dtb_node_n_size_cells(dn);
+}
+
+const uint32_t *dtb_node_get_address(const struct dtb_node *dev, int index,
+ uint64_t *size, unsigned int *flags)
+{
+ const uint32_t *prop;
+ int psize;
+ struct dtb_node *parent;
+ int onesize, i, na, ns;
+
+ /* Get parent */
+ parent = dtb_node_get_parent(dev);
+ if (parent == NULL)
+ return NULL;
+
+ dtb_node_default_count_cells(dev, &na, &ns);
+ if (!FDT_CHECK_ADDR_COUNT(na))
+ return NULL;
+
+ /* Get "reg" or "assigned-addresses" property */
+ prop = dtb_node_get_dtb_node_property_value(dev, "reg", &psize);
+ if (prop == NULL)
+ return NULL;
+ psize /= 4;
+
+ onesize = na + ns;
+ for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
+ if (i == index)
+ {
+ if (size)
+ *size = dtb_node_read_number(prop + na, ns);
+ if (flags)
+ *flags = 0x200;
+ return prop;
+ }
+ return NULL;
+} \ No newline at end of file
diff --git a/components/drivers/fdt/src/dtb_base.c b/components/drivers/fdt/src/dtb_base.c
new file mode 100644
index 0000000000..d89e5dad9e
--- /dev/null
+++ b/components/drivers/fdt/src/dtb_base.c
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "dtb_node.h"
+#include "libfdt.h"
+#include "libfdt_env.h"
+#include <ctype.h>
+
+static const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
+{
+ if (*base == 0)
+ {
+ if (s[0] == '0')
+ {
+ if (tolower(s[1]) == 'x' && isxdigit(s[2]))
+ *base = 16;
+ else
+ *base = 8;
+ }
+ else
+ *base = 10;
+ }
+ if (*base == 16 && s[0] == '0' && tolower(s[1]) == 'x')
+ s += 2;
+ return s;
+}
+
+unsigned long simple_strtoul(const char *cp, char **endp,
+ unsigned int base)
+{
+ unsigned long result = 0;
+ unsigned long value;
+
+ cp = _parse_integer_fixup_radix(cp, &base);
+
+ while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
+ ? toupper(*cp) : *cp)-'A'+10) < base)
+ {
+ result = result*base + value;
+ cp++;
+ }
+
+ if (endp)
+ *endp = (char *)cp;
+
+ return result;
+}
+
+int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
+{
+ char *tail;
+ unsigned long val;
+ size_t len;
+
+ *res = 0;
+ len = strlen(cp);
+ if (len == 0)
+ return -EINVAL;
+
+ val = simple_strtoul(cp, &tail, base);
+ if (tail == cp)
+ return -EINVAL;
+
+ if ((*tail == '\0') ||
+ ((len == (size_t)(tail - cp) + 1) && (*tail == '\n')))
+ {
+ *res = val;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+long simple_strtol(const char *cp, char **endp, unsigned int base)
+{
+ if (*cp == '-')
+ return -simple_strtoul(cp + 1, endp, base);
+
+ return simple_strtoul(cp, endp, base);
+}
+
+rt_bool_t dtb_node_read_bool(const struct dtb_node *node, const char *propname)
+{
+ const void *prop;
+
+ RT_ASSERT(dtb_node_valid(node));
+ debug("%s: %s: ", __func__, propname);
+
+ prop = dtb_node_get_property(node, propname, NULL);
+
+ debug("%s\n", prop ? "true" : "false");
+
+ return prop ? RT_TRUE : RT_FALSE;
+}
+
+const void *dtb_node_read_prop(const struct dtb_node *node, const char *propname, int *sizep)
+{
+ const char *val = NULL;
+ int len;
+
+ RT_ASSERT(dtb_node_valid(node));
+ debug("%s: %s: ", __func__, propname);
+
+ struct dtb_property *prop = dtb_node_get_dtb_node_property(node, propname, &len);
+
+ if (prop)
+ {
+ val = prop->value;
+ len = prop->size;
+ }
+
+ if (!val)
+ {
+ debug("<not found>\n");
+ if (sizep)
+ *sizep = -FDT_ERR_NOTFOUND;
+ return NULL;
+ }
+ if (sizep)
+ *sizep = len;
+
+ return val;
+}
+
+const char *dtb_node_read_string(const struct dtb_node *node, const char *propname)
+{
+ const char *str;
+ int len;
+
+ str = dtb_node_read_prop(node, propname, &len);
+ if (!str)
+ return NULL;
+
+ if (strnlen(str, len) >= len)
+ {
+ debug("<invalid>\n");
+ return NULL;
+ }
+ debug("%s\n", str);
+
+ return str;
+}
+
+const struct dtb_node *dtb_node_find_subnode(const struct dtb_node *node, const char *subnode_name)
+{
+ const struct dtb_node *subnode;
+
+ RT_ASSERT(dtb_node_valid(node));
+ debug("%s: %s: ", __func__, subnode_name);
+
+ for (node = node->child; node; node = node->sibling)
+ {
+ if (!strcmp(subnode_name, node->name))
+ break;
+ }
+ subnode = node;
+
+ debug("%s\n", dtb_node_valid(subnode) ?\
+ dtb_node_get_name(subnode) : "<none>");
+
+ return subnode;
+}
+
+struct dtb_node *dtb_node_first_subnode(const struct dtb_node *node)
+{
+ RT_ASSERT(dtb_node_valid(node));
+
+ return node->child;
+}
+
+struct dtb_node *dtb_node_next_subnode(const struct dtb_node *node)
+{
+ RT_ASSERT(dtb_node_valid(node));
+
+ return node->sibling;
+}
+
+const char *dtb_node_get_name(const struct dtb_node *node)
+{
+ if (!dtb_node_valid(node))
+ {
+ debug("%s node not valid\n", __func__);
+ return NULL;
+ }
+
+ return strrchr(node->path, '/') + 1;
+
+}
+
+struct dtb_node *dtb_node_get_by_phandle(uint phandle)
+{
+ if (dtb_node_active())
+ return dtb_node_find_node_by_phandle(phandle);
+ return NULL;
+}
+
+int dtb_node_read_size(const struct dtb_node *node, const char *propname)
+{
+ struct dtb_property *prop = dtb_node_get_dtb_node_property( node, propname, NULL);
+
+ if (prop)
+ return prop->size;
+
+ return -EINVAL;
+}
+
+size_t dtb_node_get_addr_index(const struct dtb_node *node, int index)
+{
+ int na;
+ size_t size;
+
+ const uint32_t *prop_val;
+ uint flags;
+
+ prop_val = dtb_node_get_address(node, index,
+ (uint64_t *)&size, &flags);
+ if (!prop_val)
+ return -1;
+
+ na = dtb_node_n_addr_cells(node);
+
+ return dtb_node_read_number(prop_val, na);
+}
+
+size_t dtb_node_get_addr(const struct dtb_node *node)
+{
+ return dtb_node_get_addr_index(node, 0);
+}
+
+int dtb_node_stringlist_search(const struct dtb_node *node, const char *property,
+ const char *string)
+{
+ return dtb_node_property_match_string(node, property, string);
+}
+
+int dtb_node_read_string_index(const struct dtb_node *node, const char *property, int index,
+ const char **outp)
+{
+ return dtb_node_property_read_string_index(node, property, index, outp);
+}
+
+int dtb_node_read_string_count(const struct dtb_node *node, const char *property)
+{
+ return dtb_node_property_count_strings(node, property);
+}
+
+struct dtb_node *dtb_node_path(const char *path)
+{
+ if (dtb_node_active())
+ return dtb_node_find_node_by_path(path);
+ return NULL;
+}
+
+const char *dtb_node_get_chosen_prop(const char *name)
+{
+ const struct dtb_node *chosen_node;
+
+ chosen_node = (const struct dtb_node *)dtb_node_path("/chosen");
+
+ return dtb_node_read_string(chosen_node, name);
+}
+
+struct dtb_node *dtb_node_get_chosen_node(const char *name)
+{
+ const char *prop;
+
+ prop = dtb_node_get_chosen_prop(name);
+ if (!prop)
+ return NULL;
+
+ return dtb_node_path(prop);
+}
+
+const void *dtb_node_get_property(const struct dtb_node *node, const char *propname, int *lenp)
+{
+ return dtb_node_get_dtb_node_property_value(node, propname, lenp);
+}
+
+rt_bool_t dtb_node_is_available(const struct dtb_node *node)
+{
+ return dtb_node_device_is_available(node);
+}
+
+size_t dtb_node_get_addr_size(const struct dtb_node *node, const char *property,
+ size_t *sizep)
+{
+ int na, ns;
+ int psize;
+ const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, property, &psize);
+
+ if (!prop)
+ return -1;
+ na = dtb_node_n_addr_cells(node);
+ ns = dtb_node_n_size_cells(node);
+ *sizep = dtb_node_read_number(prop + na, ns);
+
+ return dtb_node_read_number(prop, na);
+}
+
+const uint8_t *dtb_node_read_u8_array_ptr(const struct dtb_node *node, const char *propname,
+ size_t sz)
+{
+ int psize;
+ const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, propname, &psize);
+
+ if (!prop || sz != psize)
+ return NULL;
+ return (uint8_t *)prop;
+}
+
+int dtb_node_find_all_compatible_node(const struct dtb_node *from, const char *compatible, struct dtb_node **node_table, int max_num, int *node_num)
+{
+ const struct dtb_node *dn;
+ int num = 0;
+ for_each_of_allnodes_from(from, dn)
+ {
+ if (dtb_node_get_dtb_node_compatible_match(dn, compatible) &&
+ dtb_node_get(dn))
+ {
+
+ num++;
+ *node_table = (struct dtb_node *)dn;
+ node_table++;
+ if (num >= max_num)
+ {
+ break;
+ }
+ }
+ }
+ *node_num = num;
+ dtb_node_put(from);
+
+ return 0;
+}
+
+int dtb_node_write_prop(const struct dtb_node *node, const char *propname, int len,
+ const void *value)
+{
+ struct dtb_property *pp;
+ struct dtb_property *pp_last = NULL;
+ struct dtb_property *new;
+
+ if (!dtb_node_active())
+ return -ENOSYS;
+
+ if (!node)
+ return -EINVAL;
+
+ for (pp = node->properties; pp; pp = pp->next)
+ {
+ if (strcmp(pp->name, propname) == 0)
+ {
+ /* Property exists -> change value */
+ pp->value = (void *)value;
+ pp->size = len;
+ return 0;
+ }
+ pp_last = pp;
+ }
+
+ if (!pp_last)
+ return -ENOENT;
+
+ /* Property does not exist -> append new property */
+ new = malloc(sizeof(struct dtb_property));
+ if (!new)
+ return -ENOMEM;
+
+ new->name = strdup(propname);
+ if (!new->name)
+ {
+ free(new);
+ return -ENOMEM;
+ }
+
+ new->value = (void *)value;
+ new->size = len;
+ new->next = NULL;
+
+ pp_last->next = new;
+
+ return 0;
+}
+
+int dtb_node_write_string(const struct dtb_node *node, const char *propname, const char *value)
+{
+ if (!dtb_node_active())
+ return -ENOSYS;
+
+ RT_ASSERT(dtb_node_valid(node));
+
+ debug("%s: %s = %s", __func__, propname, value);
+
+ return dtb_node_write_prop(node, propname, strlen(value) + 1, value);
+}
+
+int dtb_node_set_enabled(const struct dtb_node *node, rt_bool_t value)
+{
+ if (!dtb_node_active())
+ return -ENOSYS;
+
+ RT_ASSERT(dtb_node_valid(node));
+
+ if (value)
+ return dtb_node_write_string(node, "status", "okay");
+ else
+ return dtb_node_write_string(node, "status", "disable");
+}
+
+/**
+ * dtb_node_irq_find_parent - Given a device node, find its interrupt parent node
+ * @child: pointer to device node
+ *
+ * Returns a pointer to the interrupt parent node, or NULL if the interrupt
+ * parent could not be determined.
+ */
+static struct dtb_node *dtb_node_irq_find_parent(struct dtb_node *child)
+{
+ struct dtb_node *p;
+ phandle parent;
+
+ if (!dtb_node_get(child))
+ return NULL;
+ do
+ {
+ if (dtb_node_read_u32_array(child, "interrupt-parent", &parent, 1))
+ {
+ p = dtb_node_get_parent(child);
+ }
+ else
+ {
+ p = dtb_node_get_by_phandle(parent);
+ }
+ dtb_node_put(child);
+ child = p;
+ } while (p && dtb_node_get_property(p, "#interrupt-cells", NULL) == NULL);
+
+ return p;
+}
+
+int dtb_node_irq_get(struct dtb_node *dev, int index)
+{
+ int rc = 0;
+ struct fdt_phandle_args out_irq;
+ struct dtb_node *p;
+ uint32_t intsize;
+ int res, i;
+
+ p = dtb_node_irq_find_parent(dev);
+ if (p == NULL)
+ return -EINVAL;
+ /* Get size of interrupt specifier */
+ if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
+ {
+ res = -EINVAL;
+ goto out;
+ }
+
+ debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
+
+ /* Copy intspec into irq structure */
+ out_irq.np = p;
+ out_irq.args_count = intsize;
+ for (i = 0; i < intsize; i++)
+ {
+ res = dtb_node_read_u32_index(dev, "interrupts",
+ (index * 3 + i),
+ out_irq.args + i);
+ if (res)
+ goto out;
+ }
+ rc = out_irq.args[1];
+ out:
+ dtb_node_put(p);
+ return rc;
+
+}
+
+/**
+ * dtb_node_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number
+ * @dev: pointer to device tree node
+ * @name: IRQ name
+ *
+ * Returns Linux IRQ number on success, or 0 on the IRQ mapping failure, or
+ * -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case
+ * of any other failure.
+ */
+int dtb_node_irq_get_byname(struct dtb_node *dev, const char *name)
+{
+ int index;
+
+ if (!name)
+ return -EINVAL;
+
+ index = dtb_node_stringlist_search(dev, "interrupt-names", name);
+ if (index < 0)
+ return index;
+
+ return dtb_node_irq_get(dev, index);
+}
+
+/**
+ * dtb_node_irq_count - Count the number of IRQs a node uses
+ * @dev: pointer to device tree node
+ */
+int dtb_node_irq_count(struct dtb_node *device)
+{
+ struct dtb_node *p;
+ uint32_t intsize;
+ int nr = 0, res = 0;
+
+ p = dtb_node_irq_find_parent(device);
+ if (p == NULL)
+ return -EINVAL;
+
+ /* Get size of interrupt specifier */
+ if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1))
+ {
+ res = -EINVAL;
+ goto out;
+ }
+
+ debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize);
+
+ res = dtb_node_read_size(device, "interrupts");
+ if (res < 0)
+ {
+ goto out;
+ }
+ nr = res / (intsize * 4);
+ out:
+ dtb_node_put(p);
+
+ return nr;
+} \ No newline at end of file
diff --git a/components/drivers/fdt/src/dtb_get.c b/components/drivers/fdt/src/dtb_get.c
new file mode 100644
index 0000000000..df1b5f9341
--- /dev/null
+++ b/components/drivers/fdt/src/dtb_get.c
@@ -0,0 +1,817 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "libfdt.h"
+#include "dtb_node.h"
+
+static struct
+{
+ const char *ptr;
+ const char *end;
+ char *cur;
+} paths_buf = {NULL, NULL};
+static void *current_fdt;
+
+int fdt_exec_status = FDT_RET_GET_OK;
+
+int dtb_node_get_exec_status()
+{
+ return fdt_exec_status;
+}
+
+static int _dtb_node_get_dtb_properties_list(struct dtb_property *dtb_property, off_t node_off)
+{
+ /* caller alrealy checked current_fdt */
+ off_t property_off = fdt_first_property_offset(current_fdt, node_off);
+ struct fdt_property *fdt_property;
+
+ if (property_off < 0)
+ {
+ return FDT_RET_GET_EMPTY;
+ }
+
+ for (;;)
+ {
+ fdt_property = (struct fdt_property *)fdt_get_property_by_offset(current_fdt, property_off, &dtb_property->size);
+ if (fdt_property != NULL)
+ {
+ dtb_property->name = fdt_string(current_fdt, fdt32_to_cpu(fdt_property->nameoff));
+ dtb_property->value = fdt_property->data;
+ dtb_property->size = fdt32_to_cpu(fdt_property->len);
+ }
+
+ property_off = fdt_next_property_offset(current_fdt, property_off);
+ if (property_off >= 0)
+ {
+ dtb_property->next = (struct dtb_property *)malloc(sizeof(struct dtb_property));
+ if (dtb_property->next == NULL)
+ {
+ return FDT_RET_NO_MEMORY;
+ }
+ dtb_property = dtb_property->next;
+ }
+ else
+ {
+ dtb_property->next = NULL;
+ break;
+ }
+ }
+
+ return FDT_RET_GET_OK;
+}
+
+static int _dtb_node_get_dtb_nodes_list(struct dtb_node *dtb_node_head, struct dtb_node *dtb_node, const char *pathname)
+{
+ off_t root_off;
+ off_t node_off;
+ int pathname_sz;
+ int node_name_sz;
+
+ /* caller alrealy checked current_fdt */
+ if ((root_off = fdt_path_offset(current_fdt, pathname)) >= 0)
+ {
+ pathname_sz = strlen(pathname);
+ node_off = fdt_first_subnode(current_fdt, root_off);
+
+ if (node_off < 0)
+ {
+ return FDT_RET_GET_EMPTY;
+ }
+
+ for (;;)
+ {
+ dtb_node->parent = dtb_node_head;
+ dtb_node->sibling = NULL;
+ dtb_node->name = fdt_get_name(current_fdt, node_off, &node_name_sz);
+
+ /* parent_path + name + '/' + '\0' */
+ if (paths_buf.cur + pathname_sz + node_name_sz + 2 < paths_buf.end)
+ {
+ dtb_node->path = (const char *)paths_buf.cur;
+ strncpy(paths_buf.cur, pathname, pathname_sz);
+ paths_buf.cur += pathname_sz;
+ strncpy(paths_buf.cur, (char *)dtb_node->name, node_name_sz);
+ paths_buf.cur += node_name_sz;
+ *paths_buf.cur++ = '/';
+ *paths_buf.cur++ = '\0';
+ }
+ else
+ {
+ dtb_node->path = NULL;
+ rt_kprintf("\033[31m\rERROR: `FDT_DTB_ALL_NODES_PATH_SIZE' = %d bytes is configured too low.\033[0m\n", FDT_DTB_ALL_NODES_PATH_SIZE);
+ return FDT_RET_NO_MEMORY;
+ }
+
+ dtb_node->handle = fdt_get_phandle(current_fdt, node_off);
+ dtb_node->properties = (struct dtb_property *)malloc(sizeof(struct dtb_property));
+ dtb_node->child = (struct dtb_node *)malloc(sizeof(struct dtb_node));
+
+ if (dtb_node->properties == NULL || dtb_node->child == NULL)
+ {
+ return FDT_RET_NO_MEMORY;
+ }
+
+ fdt_exec_status = _dtb_node_get_dtb_properties_list(dtb_node->properties, node_off);
+ if (fdt_exec_status == FDT_RET_GET_EMPTY)
+ {
+ free(dtb_node->properties);
+ dtb_node->properties = NULL;
+ }
+ else if (fdt_exec_status != FDT_RET_GET_OK)
+ {
+ return fdt_exec_status;
+ }
+
+ fdt_exec_status = _dtb_node_get_dtb_nodes_list(dtb_node, dtb_node->child, dtb_node->path);
+ if (fdt_exec_status == FDT_RET_GET_EMPTY)
+ {
+ free(dtb_node->child);
+ dtb_node->child = NULL;
+ }
+ else if (fdt_exec_status != FDT_RET_GET_OK)
+ {
+ return fdt_exec_status;
+ }
+
+ node_off = fdt_next_subnode(current_fdt, node_off);
+ if (node_off >= 0)
+ {
+ dtb_node->sibling = (struct dtb_node *)malloc(sizeof(struct dtb_node));
+ if (dtb_node->sibling == NULL)
+ {
+ return FDT_RET_NO_MEMORY;
+ }
+ dtb_node = dtb_node->sibling;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return FDT_RET_GET_OK;
+}
+
+struct dtb_node *dtb_node_get_dtb_list(void *fdt)
+{
+ int root_off;
+ struct dtb_node *dtb_node_head = NULL;
+
+ if (fdt == NULL)
+ {
+ fdt_exec_status = FDT_RET_NO_LOADED;
+ goto fail;
+ }
+
+ current_fdt = fdt;
+
+ if ((dtb_node_head = (struct dtb_node *)malloc(sizeof(struct dtb_node))) == NULL)
+ {
+ fdt_exec_status = FDT_RET_NO_MEMORY;
+ goto fail;
+ }
+
+ if (paths_buf.ptr == NULL)
+ {
+ paths_buf.ptr = malloc(FDT_DTB_ALL_NODES_PATH_SIZE);
+ if (paths_buf.ptr == NULL)
+ {
+ fdt_exec_status = FDT_RET_NO_MEMORY;
+ goto fail;
+ }
+ paths_buf.cur = (char *)paths_buf.ptr;
+ paths_buf.end = (char *)(paths_buf.ptr + FDT_DTB_ALL_NODES_PATH_SIZE);
+ }
+
+ root_off = fdt_path_offset(fdt, "/");
+
+ if ((dtb_node_head->header = malloc(sizeof(struct dtb_header))) == NULL)
+ {
+ fdt_exec_status = FDT_RET_NO_MEMORY;
+ goto fail;
+ }
+ else
+ {
+ ((struct dtb_header *)dtb_node_head->header)->root = '/';
+ ((struct dtb_header *)dtb_node_head->header)->zero = '\0';
+ ((struct dtb_header *)dtb_node_head->header)->memreserve_sz = fdt_num_mem_rsv(fdt);
+
+ if (dtb_node_head->header->memreserve_sz > 0)
+ {
+ int i;
+ int memreserve_sz = dtb_node_head->header->memreserve_sz;
+ uint32_t off_mem_rsvmap = fdt_off_mem_rsvmap(fdt);
+ struct fdt_reserve_entry *rsvmap = (struct fdt_reserve_entry *)((char *)fdt + off_mem_rsvmap);
+
+ ((struct dtb_header *)dtb_node_head->header)->memreserve = malloc(sizeof(struct dtb_memreserve) * memreserve_sz);
+ if (dtb_node_head->header->memreserve == NULL)
+ {
+ fdt_exec_status = FDT_RET_NO_MEMORY;
+ goto fail;
+ }
+ for (i = 0; i < memreserve_sz; ++i)
+ {
+ ((struct dtb_header *)dtb_node_head->header)->memreserve[i].address = fdt64_to_cpu(rsvmap[i].address);
+ ((struct dtb_header *)dtb_node_head->header)->memreserve[i].size = fdt64_to_cpu(rsvmap[i].size);
+ }
+ }
+ else
+ {
+ ((struct dtb_header *)dtb_node_head->header)->memreserve = NULL;
+ }
+ }
+
+ dtb_node_head->path = paths_buf.ptr;
+ *paths_buf.cur++ = '/';
+ *paths_buf.cur++ = '\0';
+ dtb_node_head->parent = NULL;
+ dtb_node_head->sibling = NULL;
+
+ dtb_node_head->handle = fdt_get_phandle(fdt, root_off);
+ dtb_node_head->properties = (struct dtb_property *)malloc(sizeof(struct dtb_property));
+ dtb_node_head->child = (struct dtb_node *)malloc(sizeof(struct dtb_node));
+
+ if (dtb_node_head->properties == NULL || dtb_node_head->child == NULL)
+ {
+ fdt_exec_status = FDT_RET_NO_MEMORY;
+ goto fail;
+ }
+
+ if ((fdt_exec_status = _dtb_node_get_dtb_properties_list(dtb_node_head->properties, root_off)) != FDT_RET_GET_OK)
+ {
+ goto fail;
+ }
+
+ if ((fdt_exec_status = _dtb_node_get_dtb_nodes_list(dtb_node_head, dtb_node_head->child, dtb_node_head->path)) != FDT_RET_GET_OK)
+ {
+ goto fail;
+ }
+
+ /* paths_buf.ptr addr save in the dtb_node_head's path */
+ paths_buf.ptr = NULL;
+ paths_buf.cur = NULL;
+
+ return dtb_node_head;
+
+fail:
+ if (dtb_node_head != NULL)
+ {
+ dtb_node_free_dtb_list(dtb_node_head);
+ }
+
+ return NULL;
+}
+
+static void _dtb_node_free_dtb_node(struct dtb_node *dtb_node)
+{
+ struct dtb_node *dtb_node_last;
+ struct dtb_property *dtb_property;
+ struct dtb_property *dtb_property_last;
+
+ while (dtb_node != NULL)
+ {
+ dtb_property = dtb_node->properties;
+ while (dtb_property != NULL)
+ {
+ dtb_property_last = dtb_property;
+ dtb_property = dtb_property->next;
+ free(dtb_property_last);
+ }
+
+ _dtb_node_free_dtb_node(dtb_node->child);
+
+ dtb_node_last = dtb_node;
+ dtb_node = dtb_node->sibling;
+ free(dtb_node_last);
+ }
+}
+
+void dtb_node_free_dtb_list(struct dtb_node *dtb_node_head)
+{
+ if (dtb_node_head == NULL)
+ {
+ return;
+ }
+
+ /* only root node can free all path buffer */
+ if (dtb_node_head->parent == NULL || (dtb_node_head->path != NULL && !strcmp(dtb_node_head->path, "/")))
+ {
+ if (dtb_node_head->path != NULL)
+ {
+ free((void *)dtb_node_head->path);
+ }
+ if (dtb_node_head->header != NULL)
+ {
+ if (dtb_node_head->header->memreserve != NULL)
+ {
+ free((void *)dtb_node_head->header->memreserve);
+ }
+ free((void *)dtb_node_head->header);
+ }
+ }
+
+ _dtb_node_free_dtb_node(dtb_node_head);
+}
+
+static void _dtb_node_printf_depth(int depth)
+{
+ int i = depth;
+ while (i --> 0)
+ {
+ rt_kputs("\t");
+ }
+}
+
+rt_bool_t _dtb_node_test_string_list(const void *value, int size)
+{
+ const char *str = value;
+ const char *str_start, *str_end;
+
+ if (size == 0)
+ {
+ return RT_FALSE;
+ }
+
+ /* string end with '\0' */
+ if (str[size - 1] != '\0')
+ {
+ return RT_FALSE;
+ }
+
+ /* get string list end */
+ str_end = str + size;
+
+ while (str < str_end)
+ {
+ str_start = str;
+ /* before string list end, not '\0' and a printable characters */
+ while (str < str_end && *str && ((unsigned char)*str >= ' ' && (unsigned char)*str <= '~'))
+ {
+ ++str;
+ }
+
+ /* not zero, or not increased */
+ if (*str != '\0' || str == str_start)
+ {
+ return RT_FALSE;
+ }
+
+ /* next string */
+ ++str;
+ }
+
+ return RT_TRUE;
+}
+
+static void _dtb_node_printf_dtb_node_info(struct dtb_node *dtb_node)
+{
+ static int depth = 0;
+ struct dtb_property *dtb_property;
+
+ while (dtb_node != NULL)
+ {
+ rt_kputs("\n");
+ _dtb_node_printf_depth(depth);
+ rt_kputs(dtb_node->name);
+ rt_kputs(" {\n");
+ ++depth;
+
+ dtb_property = dtb_node->properties;
+ while (dtb_property != NULL)
+ {
+ _dtb_node_printf_depth(depth);
+
+ rt_kputs(dtb_property->name);
+
+ if (dtb_property->size > 0)
+ {
+ int size = dtb_property->size;
+ char *value = dtb_property->value;
+
+ rt_kputs(" = ");
+ if (_dtb_node_test_string_list(value, size) == RT_TRUE)
+ {
+ /* print string list */
+ char *str = value;
+ do
+ {
+ rt_kprintf("\"%s\"", str);
+ str += strlen(str) + 1;
+ rt_kputs(", ");
+ } while (str < value + size);
+ rt_kputs("\b\b");
+ }
+ else if ((size % 4) == 0)
+ {
+ /* print addr and size cell */
+ int i;
+ fdt32_t *cell = (fdt32_t *)value;
+
+ rt_kputs("<");
+ for (i = 0, size /= 4; i < size; ++i)
+ {
+ rt_kprintf("0x%x ", fdt32_to_cpu(cell[i]));
+ }
+ rt_kputs("\b>");
+ }
+ else
+ {
+ /* print bytes array */
+ int i;
+ uint8_t *byte = (uint8_t *)value;
+
+ rt_kputs("[");
+ for (i = 0; i < size; ++i)
+ {
+ rt_kprintf("%02x ", *byte++);
+ }
+ rt_kputs("\b]");
+ }
+ }
+ rt_kputs(";\n");
+ dtb_property = dtb_property->next;
+ }
+
+ _dtb_node_printf_dtb_node_info(dtb_node->child);
+ dtb_node = dtb_node->sibling;
+
+ --depth;
+ _dtb_node_printf_depth(depth);
+ rt_kputs("};\n");
+ }
+}
+
+void dtb_node_get_dts_dump(struct dtb_node *dtb_node_head)
+{
+ if (dtb_node_head != NULL)
+ {
+ int i = dtb_node_head->header->memreserve_sz;
+
+ rt_kputs("/dts-v1/;\n");
+ while (i --> 0)
+ {
+ rt_kprintf("\n/memreserve/\t0x%lx 0x%zx;", dtb_node_head->header->memreserve[i].address, dtb_node_head->header->memreserve[i].size);
+ }
+
+ _dtb_node_printf_dtb_node_info(dtb_node_head);
+ }
+}
+
+static void _dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node, void (callback(struct dtb_node *dtb_node)))
+{
+ while (dtb_node != NULL)
+ {
+ callback(dtb_node);
+ _dtb_node_get_enum_dtb_node(dtb_node->child, callback);
+ dtb_node = dtb_node->sibling;
+ }
+}
+
+void dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node_head, void (callback(struct dtb_node *dtb_node)))
+{
+ if (dtb_node_head == NULL || callback == NULL)
+ {
+ return;
+ }
+
+ _dtb_node_get_enum_dtb_node(dtb_node_head, callback);
+}
+
+struct dtb_node *dtb_node_get_dtb_node_by_name_DFS(struct dtb_node *dtb_node, const char *nodename)
+{
+ struct dtb_node *dtb_node_child;
+
+ while (dtb_node != NULL)
+ {
+ if (!strcmp(nodename, dtb_node->name))
+ {
+ return dtb_node;
+ }
+
+ dtb_node_child = dtb_node_get_dtb_node_by_name_DFS(dtb_node->child, nodename);
+
+ if (dtb_node_child != NULL)
+ {
+ return dtb_node_child;
+ }
+ dtb_node = dtb_node->sibling;
+ }
+
+ return NULL;
+}
+
+struct dtb_node *dtb_node_get_dtb_node_by_name_BFS(struct dtb_node *dtb_node, const char *nodename)
+{
+ if (dtb_node != NULL)
+ {
+ struct dtb_node *dtb_node_child, *dtb_node_head = dtb_node;
+
+ while (dtb_node != NULL)
+ {
+ if (!strcmp(nodename, dtb_node->name))
+ {
+ return dtb_node;
+ }
+ dtb_node = dtb_node->sibling;
+ }
+
+ dtb_node = dtb_node_head;
+
+ while (dtb_node != NULL)
+ {
+ dtb_node_child = dtb_node_get_dtb_node_by_name_BFS(dtb_node->child, nodename);
+
+ if (dtb_node_child != NULL)
+ {
+ return dtb_node_child;
+ }
+ dtb_node = dtb_node->sibling;
+ }
+ }
+
+ return NULL;
+}
+
+struct dtb_node *dtb_node_get_dtb_node_by_path(struct dtb_node *dtb_node, const char *pathname)
+{
+ int i = 0;
+ char *node_name;
+ char *pathname_clone;
+ int pathname_sz;
+
+ if (pathname == NULL || dtb_node == NULL)
+ {
+ return NULL;
+ }
+
+ /* skip '/' */
+ if (*pathname == '/')
+ {
+ ++pathname;
+ }
+
+ /* root not have sibling, so skip */
+ if (dtb_node->parent == NULL || !strcmp(dtb_node->path, "/"))
+ {
+ dtb_node = dtb_node->child;
+ }
+
+ pathname_sz = strlen(pathname) + 1;
+ pathname_clone = malloc(pathname_sz);
+ if (pathname_clone == NULL)
+ {
+ return NULL;
+ }
+ strncpy(pathname_clone, pathname, pathname_sz);
+ node_name = pathname_clone;
+
+ while (pathname_clone[i])
+ {
+ if (pathname_clone[i] == '/')
+ {
+ /* set an end of name that can used to strcmp */
+ pathname_clone[i] = '\0';
+
+ while (dtb_node != NULL)
+ {
+ if (!strcmp(dtb_node->name, node_name))
+ {
+ break;
+ }
+ dtb_node = dtb_node->sibling;
+ }
+ if (dtb_node == NULL)
+ {
+ free(pathname_clone);
+ return NULL;
+ }
+ dtb_node = dtb_node->child;
+ node_name = &pathname_clone[i + 1];
+ }
+ ++i;
+ }
+
+ /*
+ * found the end node and it's name:
+ * (pathname[pathname_sz - 1]) is '\0'
+ * (&pathname_clone[i] - node_name) is the node_name's length
+ */
+ node_name = &((char *)pathname)[(pathname_sz - 1) - (&pathname_clone[i] - node_name)];
+ free(pathname_clone);
+
+ while (dtb_node != NULL)
+ {
+ if (!strcmp(dtb_node->name, node_name))
+ {
+ return dtb_node;
+ }
+ dtb_node = dtb_node->sibling;
+ }
+
+ return NULL;
+}
+
+struct dtb_node *dtb_node_get_dtb_node_by_phandle_DFS(struct dtb_node *dtb_node, phandle handle)
+{
+ struct dtb_node *dtb_node_child;
+
+ while (dtb_node != NULL)
+ {
+ if (dtb_node->handle == handle)
+ {
+ return dtb_node;
+ }
+
+ dtb_node_child = dtb_node_get_dtb_node_by_phandle_DFS(dtb_node->child, handle);
+
+ if (dtb_node_child != NULL)
+ {
+ return dtb_node_child;
+ }
+ dtb_node = dtb_node->sibling;
+ }
+
+ return NULL;
+}
+
+struct dtb_node *dtb_node_get_dtb_node_by_phandle_BFS(struct dtb_node *dtb_node, phandle handle)
+{
+ if (dtb_node != NULL)
+ {
+ struct dtb_node *dtb_node_child, *dtb_node_head = dtb_node;
+
+ while (dtb_node != NULL)
+ {
+ if (dtb_node->handle == handle)
+ {
+ return dtb_node;
+ }
+ dtb_node = dtb_node->sibling;
+ }
+
+ dtb_node = dtb_node_head;
+
+ while (dtb_node != NULL)
+ {
+ dtb_node_child = dtb_node_get_dtb_node_by_phandle_BFS(dtb_node->child, handle);
+
+ if (dtb_node_child != NULL)
+ {
+ return dtb_node_child;
+ }
+ dtb_node = dtb_node->sibling;
+ }
+ }
+
+ return NULL;
+}
+
+void dtb_node_get_dtb_node_cells(struct dtb_node *dtb_node, int *addr_cells, int *size_cells)
+{
+ if (dtb_node != NULL && addr_cells != NULL && size_cells != NULL)
+ {
+ struct dtb_property *dtb_property;
+ *addr_cells = -1;
+ *size_cells = -1;
+
+ /* if couldn't found, check parent */
+ while ((dtb_node = dtb_node->parent) != NULL)
+ {
+ dtb_property = dtb_node->properties;
+ while (dtb_property != NULL)
+ {
+ if (!strcmp(dtb_property->name, "#address-cells"))
+ {
+ *addr_cells = fdt32_to_cpu(*(int *)dtb_property->value);
+ }
+ else if (!strcmp(dtb_property->name, "#size-cells"))
+ {
+ *size_cells = fdt32_to_cpu(*(int *)dtb_property->value);
+ }
+ if (*addr_cells != -1 && *size_cells != -1)
+ {
+ return;
+ }
+ dtb_property = dtb_property->next;
+ }
+ }
+
+ if (*addr_cells == -1)
+ {
+ *addr_cells = FDT_ROOT_ADDR_CELLS_DEFAULT;
+ }
+ if (*size_cells == -1)
+ {
+ *size_cells = FDT_ROOT_SIZE_CELLS_DEFAULT;
+ }
+ }
+}
+
+struct dtb_memreserve *dtb_node_get_dtb_memreserve(struct dtb_node *dtb_node, int *memreserve_size)
+{
+ if (dtb_node != NULL && memreserve_size != NULL)
+ {
+ struct dtb_node *dtb_node_root = dtb_node;
+
+ while (dtb_node_root != NULL)
+ {
+ if (!strcmp(dtb_node_root->path, "/"))
+ {
+ break;
+ }
+ dtb_node_root = dtb_node_root->parent;
+ }
+
+ *memreserve_size = dtb_node_root->header->memreserve_sz;
+
+ return dtb_node_root->header->memreserve;
+ }
+ return NULL;
+}
+
+rt_bool_t dtb_node_get_dtb_node_status(const struct dtb_node *dtb_node)
+{
+ if (dtb_node != NULL)
+ {
+ char *status = dtb_node_get_dtb_node_property_value(dtb_node, "status", NULL);
+ if (status != NULL)
+ {
+ return (!strcmp(status, "okay") || !strcmp(status, "ok")) ? RT_TRUE : RT_FALSE;
+ }
+
+ return RT_TRUE;
+ }
+
+ return RT_FALSE;
+}
+
+rt_bool_t dtb_node_get_dtb_node_compatible_match(const struct dtb_node *dtb_node, const char *compatibles)
+{
+ if (dtb_node != NULL)
+ {
+ if (compatibles != NULL)
+ {
+ char *str_ptr;
+ int prop_sz;
+
+ for_each_property_string(dtb_node, "compatible", str_ptr, prop_sz)
+ {
+ if (!strcmp(compatibles, str_ptr))
+ {
+ return RT_TRUE;
+ }
+ }
+ }
+ }
+
+ return RT_FALSE;
+}
+
+char *dtb_node_get_dtb_string_list_value(void *value, int size, int index)
+{
+ int i = 0;
+ char *str = value;
+
+ if (str != NULL)
+ {
+ do
+ {
+ if (i++ == index)
+ {
+ return str;
+ }
+ str += strlen(str) + 1;
+ } while (str < (char *)value + size);
+ }
+
+ return NULL;
+}
+
+char *dtb_node_get_dtb_string_list_value_next(void *value, void *end)
+{
+ char *str = value;
+
+ if (str != NULL)
+ {
+ str += strlen(str) + 1;
+ if (str < (char *)end)
+ {
+ return str;
+ }
+ }
+
+ return NULL;
+}
+
+uint32_t dtb_node_get_dtb_cell_value(void *value)
+{
+ return fdt32_to_cpu(*(fdt32_t *)value);
+}
+
+uint8_t dtb_node_get_dtb_byte_value(void *value)
+{
+ return *(uint8_t *)value;
+}
diff --git a/components/drivers/fdt/src/dtb_head.c b/components/drivers/fdt/src/dtb_head.c
new file mode 100644
index 0000000000..7fe661943d
--- /dev/null
+++ b/components/drivers/fdt/src/dtb_head.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "dtb_node.h"
+
+static void *dtb_root = NULL;
+static struct dtb_node *dtb_node_list = NULL;
+void *get_fdt_blob(void)
+{
+ return dtb_root;
+}
+
+struct dtb_node *get_dtb_node_head(void)
+{
+ return dtb_node_list;
+}
+
+rt_bool_t dtb_node_active(void)
+{
+ return dtb_node_list != NULL;
+}
+
+int device_tree_setup(void *mem_addr)
+{
+ if(mem_addr)
+ {
+ if ((dtb_root = dtb_node_load_from_memory(mem_addr,1)) != NULL)
+ {
+ dtb_node_list = dtb_node_get_dtb_list(dtb_root);
+ if (dtb_node_list != NULL)
+ {
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ return -1;
+} \ No newline at end of file
diff --git a/components/drivers/fdt/src/dtb_load.c b/components/drivers/fdt/src/dtb_load.c
new file mode 100644
index 0000000000..a6c6dca6f9
--- /dev/null
+++ b/components/drivers/fdt/src/dtb_load.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "libfdt.h"
+#include <unistd.h>
+#include "dtb_node.h"
+
+extern int fdt_exec_status;
+
+rt_bool_t dtb_node_check(void *fdt)
+{
+ return fdt_check_header(fdt) == 0 ? RT_TRUE : RT_FALSE;
+}
+
+void *dtb_node_load_from_fs(char *dtb_filename)
+{
+ void *fdt = NULL;
+ size_t dtb_sz;
+ int fd = -1;
+
+ if (dtb_filename == NULL)
+ {
+ fdt_exec_status = FDT_RET_GET_EMPTY;
+ goto end;
+ }
+
+ fd = open(dtb_filename, O_RDONLY, 0);
+
+ if (fd == -1)
+ {
+ rt_kprintf("File `%s' not found.\n", dtb_filename);
+ fdt_exec_status = FDT_RET_GET_EMPTY;
+ goto end;
+ }
+
+ dtb_sz = lseek(fd, 0, SEEK_END);
+
+ if (dtb_sz > 0)
+ {
+ if ((fdt = (struct fdt_header *)malloc(sizeof(uint8_t) * dtb_sz)) == NULL)
+ {
+ fdt_exec_status = FDT_RET_NO_MEMORY;
+ goto end;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+ read(fd, fdt, sizeof(uint8_t) * dtb_sz);
+
+ if (dtb_node_check(fdt) == RT_FALSE)
+ {
+ free(fdt);
+ }
+ }
+
+end:
+ if (fd != -1)
+ {
+ close(fd);
+ }
+
+ return fdt;
+}
+
+void *dtb_node_load_from_memory(void *dtb_ptr, rt_bool_t is_clone)
+{
+ void *fdt = NULL;
+
+ if (dtb_ptr == NULL)
+ {
+ fdt_exec_status = FDT_RET_GET_EMPTY;
+ goto end;
+ }
+
+ if (dtb_node_check(dtb_ptr) == RT_FALSE)
+ {
+ fdt_exec_status = FDT_RET_GET_EMPTY;
+ fdt = NULL;
+ goto end;
+ }
+
+ if (is_clone)
+ {
+ size_t dtb_sz = fdt_totalsize(dtb_ptr);
+ if (dtb_sz > 0)
+ {
+ if ((fdt = malloc(dtb_sz)) != NULL)
+ {
+ memcpy(fdt, dtb_ptr, dtb_sz);
+ }
+ else
+ {
+ fdt_exec_status = FDT_RET_NO_MEMORY;
+ }
+ }
+ }
+ else
+ {
+ fdt = dtb_ptr;
+ }
+
+end:
+ return fdt;
+}
diff --git a/components/drivers/fdt/src/dtb_set.c b/components/drivers/fdt/src/dtb_set.c
new file mode 100644
index 0000000000..28fe2b5fde
--- /dev/null
+++ b/components/drivers/fdt/src/dtb_set.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "libfdt.h"
+#include "dtb_node.h"
+
+static off_t dtb_node_find_and_add_subnode(void *fdt, char* name)
+{
+ off_t chosen_offset = 0;
+
+ chosen_offset = fdt_subnode_offset(fdt, 0, name);
+
+ if (chosen_offset == -FDT_ERR_NOTFOUND)
+ {
+ chosen_offset = fdt_add_subnode(fdt, 0, name);
+ }
+
+ return chosen_offset;
+}
+
+size_t dtb_node_set_linux_cmdline(void *fdt, char *cmdline)
+{
+ off_t chosen_offset;
+ size_t cmdline_size;
+
+ if (cmdline == NULL || fdt == NULL)
+ {
+ goto end;
+ }
+
+ chosen_offset = dtb_node_find_and_add_subnode(fdt, "chosen");
+ cmdline_size = strlen(cmdline);
+
+ /* install bootargs */
+ if (chosen_offset >= 0 || chosen_offset == -FDT_ERR_EXISTS)
+ {
+ if (fdt_setprop(fdt, chosen_offset, "bootargs", cmdline, cmdline_size) < 0)
+ {
+ fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + FDT_DTB_PAD_SIZE);
+ fdt_setprop(fdt, chosen_offset, "bootargs", cmdline, cmdline_size);
+ }
+ }
+
+end:
+ return fdt_totalsize(fdt);
+}
+
+size_t dtb_node_set_linux_initrd(void *fdt, uint64_t initrd_addr, size_t initrd_size)
+{
+ uint64_t addr, size_ptr;
+ off_t chosen_offset;
+ int i;
+
+ if (fdt == NULL)
+ {
+ goto end;
+ }
+
+ chosen_offset = dtb_node_find_and_add_subnode(fdt, "chosen");
+
+ /* update the entry */
+ for (i = fdt_num_mem_rsv(fdt) - 1; i >= 0; --i)
+ {
+ fdt_get_mem_rsv(fdt, i, &addr, &size_ptr);
+ if (addr == initrd_addr)
+ {
+ fdt_del_mem_rsv(fdt, i);
+ break;
+ }
+ }
+
+ /* add the memory */
+ if (fdt_add_mem_rsv(fdt, initrd_addr, initrd_size) < 0)
+ {
+ /* move the memory */
+ fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + FDT_DTB_PAD_SIZE);
+ if (fdt_add_mem_rsv(fdt, initrd_addr, initrd_size) < 0)
+ {
+ goto end;
+ }
+ }
+
+ /* install initrd */
+ if (chosen_offset >= 0 || chosen_offset == -FDT_ERR_EXISTS)
+ {
+ chosen_offset = fdt_path_offset(fdt, "/chosen");
+
+ if (IN_64BITS_MODE)
+ {
+ fdt_setprop_u64(fdt, chosen_offset, "linux,initrd-start", initrd_addr);
+ fdt_setprop_u64(fdt, chosen_offset, "linux,initrd-end", initrd_addr + initrd_size);
+ }
+ else
+ {
+ fdt_setprop_u32(fdt, chosen_offset, "linux,initrd-start", initrd_addr);
+ fdt_setprop_u32(fdt, chosen_offset, "linux,initrd-end", initrd_addr + initrd_size);
+ }
+ }
+
+end:
+ return fdt_totalsize(fdt);
+}
+
+size_t dtb_node_set_dtb_property(void *fdt, char *pathname, char *property_name, uint32_t *cells, size_t cells_size)
+{
+ int node_off;
+
+ if (fdt == NULL)
+ {
+ goto end;
+ }
+
+ node_off = fdt_path_offset(fdt, pathname);
+
+ if (node_off >= 0 && cells_size != 0)
+ {
+ fdt_setprop(fdt, node_off, property_name, cells, cells_size);
+ }
+
+end:
+ return fdt_totalsize(fdt);
+}
+
+size_t dtb_node_add_dtb_memreserve(void *fdt, uint64_t address, uint64_t size)
+{
+ if (fdt == NULL)
+ {
+ goto end;
+ }
+
+ fdt_add_mem_rsv(fdt, address, size);
+
+end:
+ return fdt_totalsize(fdt);
+}
+
+size_t dtb_node_del_dtb_memreserve(void *fdt, uint64_t address)
+{
+ int i;
+ int num_mem_rsvmap;
+ uint32_t off_mem_rsvmap;
+ struct fdt_reserve_entry *rsvmap;
+
+ if (fdt == NULL)
+ {
+ goto end;
+ }
+
+ num_mem_rsvmap = fdt_num_mem_rsv(fdt);
+ off_mem_rsvmap = fdt_off_mem_rsvmap(fdt);
+ rsvmap = (struct fdt_reserve_entry *)((char *)fdt + off_mem_rsvmap);
+
+ for (i = 0; i < num_mem_rsvmap; ++i)
+ {
+ if (address == fdt64_to_cpu(rsvmap[i].address))
+ {
+ fdt_del_mem_rsv(fdt, i);
+ break;
+ }
+ }
+
+end:
+ return fdt_totalsize(fdt);
+}
diff --git a/components/drivers/include/drivers/cputime.h b/components/drivers/include/drivers/cputime.h
index ec98f3152e..bcc339ca35 100644
--- a/components/drivers/include/drivers/cputime.h
+++ b/components/drivers/include/drivers/cputime.h
@@ -22,8 +22,8 @@ struct rt_clock_cputime_ops
float clock_cpu_getres(void);
uint64_t clock_cpu_gettime(void);
-uint32_t clock_cpu_microsecond(uint32_t cpu_tick);
-uint32_t clock_cpu_millisecond(uint32_t cpu_tick);
+uint64_t clock_cpu_microsecond(uint64_t cpu_tick);
+uint64_t clock_cpu_millisecond(uint64_t cpu_tick);
int clock_cpu_setops(const struct rt_clock_cputime_ops *ops);
diff --git a/components/drivers/include/drivers/gpt.h b/components/drivers/include/drivers/gpt.h
new file mode 100644
index 0000000000..c1bdcb732a
--- /dev/null
+++ b/components/drivers/include/drivers/gpt.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-05-05 linzhenxing first version
+ */
+#ifndef __GPT_H
+#define __GPT_H
+
+#include <rtthread.h>
+#include <stdint.h>
+
+typedef struct
+{
+ uint8_t b[16]; /* GUID 16 bytes*/
+} guid_t;
+
+#define MSDOS_MBR_SIGNATURE 0xaa55
+#define EFI_PMBR_OSTYPE_EFI 0xEF
+#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE
+
+#define GPT_MBR_PROTECTIVE 1
+#define GPT_MBR_HYBRID 2
+
+#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL
+#define GPT_HEADER_REVISION_V1 0x00010000
+#define GPT_PRIMARY_PARTITION_TABLE_LBA 1
+
+typedef guid_t gpt_guid_t __attribute__ ((aligned (4)));
+#define EFI_GUID(a, b, c, d...) (gpt_guid_t){ { \
+ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \
+ (b) & 0xff, ((b) >> 8) & 0xff, \
+ (c) & 0xff, ((c) >> 8) & 0xff, d } }
+
+#define NULL_GUID \
+ EFI_GUID(0x00000000, 0x0000, 0x0000,\
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
+#define PARTITION_SYSTEM_GUID \
+ EFI_GUID( 0xC12A7328, 0xF81F, 0x11d2, \
+ 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B)
+#define LEGACY_MBR_PARTITION_GUID \
+ EFI_GUID( 0x024DEE41, 0x33E7, 0x11d3, \
+ 0x9D, 0x69, 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F)
+#define PARTITION_MSFT_RESERVED_GUID \
+ EFI_GUID( 0xE3C9E316, 0x0B5C, 0x4DB8, \
+ 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE)
+#define PARTITION_BASIC_DATA_GUID \
+ EFI_GUID( 0xEBD0A0A2, 0xB9E5, 0x4433, \
+ 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7)
+#define PARTITION_LINUX_RAID_GUID \
+ EFI_GUID( 0xa19d880f, 0x05fc, 0x4d3b, \
+ 0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e)
+#define PARTITION_LINUX_SWAP_GUID \
+ EFI_GUID( 0x0657fd6d, 0xa4ab, 0x43c4, \
+ 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f)
+#define PARTITION_LINUX_LVM_GUID \
+ EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \
+ 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28)
+#pragma pack(push, 1)
+typedef struct _gpt_header
+{
+ uint64_t signature;
+ uint32_t revision;
+ uint32_t header_size;
+ uint32_t header_crc32;
+ uint32_t reserved1;
+ uint64_t start_lba; /*GPT head start sector*/
+ uint64_t alternate_lba; /*GPT head alternate sector*/
+ uint64_t first_usable_lba;
+ uint64_t last_usable_lba;
+ gpt_guid_t disk_guid;
+ uint64_t partition_entry_lba;
+ uint32_t num_partition_entries;
+ uint32_t sizeof_partition_entry;
+ uint32_t partition_entry_array_crc32;
+
+ /* The rest of the logical block is reserved by UEFI and must be zero.
+ * EFI standard handles this by:
+ *
+ * uint8_t reserved2[ BlockSize - 92 ];
+ */
+} gpt_header;
+
+typedef struct _gpt_entry_attributes
+{
+ uint64_t required_to_function:1;
+ uint64_t reserved:47;
+ uint64_t type_guid_specific:16;
+} gpt_entry_attributes;
+
+typedef struct _gpt_entry
+{
+ gpt_guid_t partition_type_guid;
+ gpt_guid_t unique_partition_guid;
+ uint64_t starting_lba;
+ uint64_t ending_lba;
+ gpt_entry_attributes attributes;
+ uint16_t partition_name[72/sizeof(uint16_t)];
+} gpt_entry;
+
+typedef struct _gpt_mbr_record
+{
+ uint8_t boot_indicator; /* unused by EFI, set to 0x80 for bootable */
+ uint8_t start_head; /* unused by EFI, pt start in CHS */
+ uint8_t start_sector; /* unused by EFI, pt start in CHS */
+ uint8_t start_track;
+ uint8_t os_type; /* EFI and legacy non-EFI OS types */
+ uint8_t end_head; /* unused by EFI, pt end in CHS */
+ uint8_t end_sector; /* unused by EFI, pt end in CHS */
+ uint8_t end_track; /* unused by EFI, pt end in CHS */
+ uint32_t starting_lba; /* used by EFI - start addr of the on disk pt */
+ uint32_t size_in_lba; /* used by EFI - size of pt in LBA */
+} gpt_mbr_record;
+
+
+typedef struct _legacy_mbr
+{
+ uint8_t boot_code[440];
+ uint32_t unique_mbr_signature;
+ uint16_t unknown;
+ gpt_mbr_record partition_record[4];
+ uint16_t signature;
+} legacy_mbr;
+#pragma pack(pop)
+
+int check_gpt(struct rt_mmcsd_card *card);
+int gpt_get_partition_param(struct rt_mmcsd_card *card, struct dfs_partition *part, uint32_t pindex);
+void gpt_free(void);
+#endif /*__GPT_H*/
diff --git a/components/drivers/include/drivers/lcd.h b/components/drivers/include/drivers/lcd.h
new file mode 100644
index 0000000000..74d291feac
--- /dev/null
+++ b/components/drivers/include/drivers/lcd.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-03-05 bernard first version.
+ */
+
+#ifndef RT_LCD_H__
+#define RT_LCD_H__
+
+/* ioctls
+ 0x46 is 'F' */
+
+#define FBIOGET_VSCREENINFO 0x4600
+#define FBIOPUT_VSCREENINFO 0x4601
+#define FBIOGET_FSCREENINFO 0x4602
+#define FBIOGETCMAP 0x4604
+#define FBIOPUTCMAP 0x4605
+#define FBIOPAN_DISPLAY 0x4606
+#define FBIO_CURSOR 0x4608
+/* #define FBIOGET_MONITORSPEC 0x460C */
+/* #define FBIOPUT_MONITORSPEC 0x460D */
+/* #define FBIOSWITCH_MONIBIT 0x460E */
+
+#define FBIOGET_CON2FBMAP 0x460F
+#define FBIOPUT_CON2FBMAP 0x4610
+#define FBIOBLANK 0x4611 /* arg: 0 or vesa level + 1 */
+#define FBIOGET_VBLANK 0x4612
+#define FBIO_ALLOC 0x4613
+#define FBIO_FREE 0x4614
+#define FBIOGET_GLYPH 0x4615
+#define FBIOGET_HWCINFO 0x4616
+#define FBIOPUT_MODEINFO 0x4617
+#define FBIOGET_DISPINFO 0x4618
+#define FBIO_WAITFORVSYNC 0x4620
+
+struct fb_bitfield
+{
+ uint32_t offset; /* beginning of bitfield */
+ uint32_t length; /* length of bitfield */
+ uint32_t msb_right; /* != 0 : Most significant bit is */
+ /* right */
+};
+
+struct fb_var_screeninfo
+{
+ uint32_t xres; /* visible resolution */
+ uint32_t yres;
+ uint32_t xres_virtual; /* virtual resolution */
+ uint32_t yres_virtual;
+ uint32_t xoffset; /* offset from virtual to visible */
+ uint32_t yoffset; /* resolution */
+
+ uint32_t bits_per_pixel; /* guess what */
+ uint32_t grayscale; /* 0 = color, 1 = grayscale, */
+ /* >1 = FOURCC */
+ struct fb_bitfield red; /* bitfield in fb mem if true color, */
+ struct fb_bitfield green; /* else only length is significant */
+ struct fb_bitfield blue;
+ struct fb_bitfield transp; /* transparency */
+
+ uint32_t nonstd; /* != 0 Non standard pixel format */
+
+ uint32_t activate; /* see FB_ACTIVATE_* */
+
+ uint32_t height; /* height of picture in mm */
+ uint32_t width; /* width of picture in mm */
+
+ uint32_t accel_flags; /* (OBSOLETE) see fb_info.flags */
+
+ /* Timing: All values in pixclocks, except pixclock (of course) */
+ uint32_t pixclock; /* pixel clock in ps (pico seconds) */
+ uint32_t left_margin; /* time from sync to picture */
+ uint32_t right_margin; /* time from picture to sync */
+ uint32_t upper_margin; /* time from sync to picture */
+ uint32_t lower_margin;
+ uint32_t hsync_len; /* length of horizontal sync */
+ uint32_t vsync_len; /* length of vertical sync */
+ uint32_t sync; /* see FB_SYNC_* */
+ uint32_t vmode; /* see FB_VMODE_* */
+ uint32_t rotate; /* angle we rotate counter clockwise */
+ uint32_t colorspace; /* colorspace for FOURCC-based modes */
+ uint32_t reserved[4]; /* Reserved for future compatibility */
+};
+
+struct fb_fix_screeninfo
+{
+ char id[16]; /* identification string eg "TT Builtin" */
+ unsigned long smem_start; /* Start of frame buffer mem */
+ /* (physical address) */
+ uint32_t smem_len; /* Length of frame buffer mem */
+ uint32_t type; /* see FB_TYPE_* */
+ uint32_t type_aux; /* Interleave for interleaved Planes */
+ uint32_t visual; /* see FB_VISUAL_* */
+ uint16_t xpanstep; /* zero if no hardware panning */
+ uint16_t ypanstep; /* zero if no hardware panning */
+ uint16_t ywrapstep; /* zero if no hardware ywrap */
+ uint32_t line_length; /* length of a line in bytes */
+ unsigned long mmio_start; /* Start of Memory Mapped I/O */
+ /* (physical address) */
+ uint32_t mmio_len; /* Length of Memory Mapped I/O */
+ uint32_t accel; /* Indicate to driver which */
+ /* specific chip/card we have */
+ uint16_t capabilities; /* see FB_CAP_* */
+ uint16_t reserved[2]; /* Reserved for future compatibility */
+};
+
+#endif
diff --git a/components/drivers/include/drivers/mmcsd_card.h b/components/drivers/include/drivers/mmcsd_card.h
index 8a49978a17..f877e60069 100644
--- a/components/drivers/include/drivers/mmcsd_card.h
+++ b/components/drivers/include/drivers/mmcsd_card.h
@@ -141,6 +141,7 @@ struct rt_mmcsd_card {
rt_uint32_t max_data_rate; /* max data transfer rate */
rt_uint32_t card_capacity; /* card capacity, unit:KB */
rt_uint32_t card_blksize; /* card block size */
+ rt_uint32_t card_sec_cnt; /* card sector count*/
rt_uint32_t erase_size; /* erase size in sectors */
rt_uint16_t card_type;
#define CARD_TYPE_MMC 0 /* MMC card */
@@ -152,7 +153,8 @@ struct rt_mmcsd_card {
#define CARD_FLAG_HIGHSPEED (1 << 0) /* SDIO bus speed 50MHz */
#define CARD_FLAG_SDHC (1 << 1) /* SDHC card */
#define CARD_FLAG_SDXC (1 << 2) /* SDXC card */
-
+#define CARD_FLAG_HIGHSPEED_DDR (1 << 3) /*HIGH SPEED DDR*/
+#define CARD_FLAG_HS200 (1 << 4) /* BUS SPEED 200mHz*/
struct rt_sd_scr scr;
struct rt_mmcsd_csd csd;
rt_uint32_t hs_max_data_rate; /* max data transfer rate in high speed mode */
diff --git a/components/drivers/include/drivers/mmcsd_cmd.h b/components/drivers/include/drivers/mmcsd_cmd.h
index 2993fe5406..2bd7b93ee1 100644
--- a/components/drivers/include/drivers/mmcsd_cmd.h
+++ b/components/drivers/include/drivers/mmcsd_cmd.h
@@ -37,7 +37,8 @@ extern "C" {
#define SET_BLOCKLEN 16 /* ac [31:0] block len R1 */
#define READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */
#define READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */
-
+#define SEND_TUNING_BLOCK 19 /* adtc R1 */
+#define SEND_TUNING_BLOCK_HS200 21 /* adtc R1*/
/* class 3 */
#define WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
diff --git a/components/drivers/include/drivers/mmcsd_core.h b/components/drivers/include/drivers/mmcsd_core.h
index 51263d35f2..04a6ffde2b 100644
--- a/components/drivers/include/drivers/mmcsd_core.h
+++ b/components/drivers/include/drivers/mmcsd_core.h
@@ -26,7 +26,8 @@ extern "C" {
#define mmcsd_dbg(fmt, ...)
#endif
-struct rt_mmcsd_data {
+struct rt_mmcsd_data
+{
rt_uint32_t blksize;
rt_uint32_t blks;
rt_uint32_t *buf;
@@ -43,9 +44,15 @@ struct rt_mmcsd_data {
rt_uint32_t timeout_ns;
rt_uint32_t timeout_clks;
+
+ void *sg; /* scatter list */
+ rt_uint16_t sg_len; /* size of scatter list */
+ rt_int16_t sg_count; /* mapped sg entries */
+ rt_ubase_t host_cookie; /* host driver private data */
};
-struct rt_mmcsd_cmd {
+struct rt_mmcsd_cmd
+{
rt_uint32_t cmd_code;
rt_uint32_t arg;
rt_uint32_t resp[4];
@@ -94,15 +101,20 @@ struct rt_mmcsd_cmd {
rt_int32_t retries; /* max number of retries */
rt_int32_t err;
+ unsigned int busy_timeout; /* busy detect timeout in ms */
struct rt_mmcsd_data *data;
struct rt_mmcsd_req *mrq; /* associated request */
};
-struct rt_mmcsd_req {
+struct rt_mmcsd_req
+{
struct rt_mmcsd_data *data;
struct rt_mmcsd_cmd *cmd;
struct rt_mmcsd_cmd *stop;
+ struct rt_mmcsd_cmd *sbc; /* SET_BLOCK_COUNT for multiblock */
+ /* Allow other commands during this ongoing data transfer or busy wait */
+ int cap_cmd_during_tfr;
};
/*the following is response bit*/
@@ -208,6 +220,7 @@ rt_inline rt_uint32_t __rt_fls(rt_uint32_t val)
#define MMCSD_HOST_PLUGED 0
#define MMCSD_HOST_UNPLUGED 1
+rt_int32_t mmcsd_excute_tuning(struct rt_mmcsd_card *card);
int mmcsd_wait_cd_changed(rt_int32_t timeout);
void mmcsd_host_lock(struct rt_mmcsd_host *host);
void mmcsd_host_unlock(struct rt_mmcsd_host *host);
@@ -226,14 +239,18 @@ void mmcsd_set_chip_select(struct rt_mmcsd_host *host, rt_int32_t mode);
void mmcsd_set_clock(struct rt_mmcsd_host *host, rt_uint32_t clk);
void mmcsd_set_bus_mode(struct rt_mmcsd_host *host, rt_uint32_t mode);
void mmcsd_set_bus_width(struct rt_mmcsd_host *host, rt_uint32_t width);
+void mmcsd_set_timing(struct rt_mmcsd_host *host, rt_uint32_t timing);
void mmcsd_set_data_timeout(struct rt_mmcsd_data *data, const struct rt_mmcsd_card *card);
rt_uint32_t mmcsd_select_voltage(struct rt_mmcsd_host *host, rt_uint32_t ocr);
void mmcsd_change(struct rt_mmcsd_host *host);
void mmcsd_detect(void *param);
+void mmcsd_host_init(struct rt_mmcsd_host *host);
struct rt_mmcsd_host *mmcsd_alloc_host(void);
void mmcsd_free_host(struct rt_mmcsd_host *host);
int rt_mmcsd_core_init(void);
+int rt_mmcsd_blk_init(void);
+rt_int32_t read_lba(struct rt_mmcsd_card *card, size_t lba, uint8_t *buffer, size_t count);
rt_int32_t rt_mmcsd_blk_probe(struct rt_mmcsd_card *card);
void rt_mmcsd_blk_remove(struct rt_mmcsd_card *card);
diff --git a/components/drivers/include/drivers/mmcsd_host.h b/components/drivers/include/drivers/mmcsd_host.h
index 44f7b1c916..774f515c8a 100644
--- a/components/drivers/include/drivers/mmcsd_host.h
+++ b/components/drivers/include/drivers/mmcsd_host.h
@@ -17,7 +17,8 @@
extern "C" {
#endif
-struct rt_mmcsd_io_cfg {
+struct rt_mmcsd_io_cfg
+{
rt_uint32_t clock; /* clock rate */
rt_uint16_t vdd;
@@ -45,21 +46,50 @@ struct rt_mmcsd_io_cfg {
#define MMCSD_BUS_WIDTH_1 0
#define MMCSD_BUS_WIDTH_4 2
#define MMCSD_BUS_WIDTH_8 3
-#define MMCSD_DDR_BUS_WIDTH_4 4
-#define MMCSD_DDR_BUS_WIDTH_8 5
+
+ unsigned char timing; /* timing specification used */
+
+#define MMCSD_TIMING_LEGACY 0
+#define MMCSD_TIMING_MMC_HS 1
+#define MMCSD_TIMING_SD_HS 2
+#define MMCSD_TIMING_UHS_SDR12 3
+#define MMCSD_TIMING_UHS_SDR25 4
+#define MMCSD_TIMING_UHS_SDR50 5
+#define MMCSD_TIMING_UHS_SDR104 6
+#define MMCSD_TIMING_UHS_DDR50 7
+#define MMCSD_TIMING_MMC_DDR52 8
+#define MMCSD_TIMING_MMC_HS200 9
+#define MMCSD_TIMING_MMC_HS400 10
+
+ unsigned char drv_type; /* driver type (A, B, C, D) */
+
+#define MMCSD_SET_DRIVER_TYPE_B 0
+#define MMCSD_SET_DRIVER_TYPE_A 1
+#define MMCSD_SET_DRIVER_TYPE_C 2
+#define MMCSD_SET_DRIVER_TYPE_D 3
+
+ unsigned char signal_voltage;
+
+#define MMCSD_SIGNAL_VOLTAGE_330 0
+#define MMCSD_SIGNAL_VOLTAGE_180 1
+#define MMCSD_SIGNAL_VOLTAGE_120 2
};
struct rt_mmcsd_host;
struct rt_mmcsd_req;
-struct rt_mmcsd_host_ops {
+struct rt_mmcsd_host_ops
+{
void (*request)(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req);
void (*set_iocfg)(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg);
rt_int32_t (*get_card_status)(struct rt_mmcsd_host *host);
void (*enable_sdio_irq)(struct rt_mmcsd_host *host, rt_int32_t en);
+ rt_int32_t (*execute_tuning)(struct rt_mmcsd_host *host, rt_int32_t opcode);
};
-struct rt_mmcsd_host {
+struct rt_mmcsd_host
+{
+ char name[RT_NAME_MAX];
struct rt_mmcsd_card *card;
const struct rt_mmcsd_host_ops *ops;
rt_uint32_t freq_min;
@@ -90,10 +120,15 @@ struct rt_mmcsd_host {
#define MMCSD_HOST_IS_SPI (1 << 3)
#define controller_is_spi(host) (host->flags & MMCSD_HOST_IS_SPI)
#define MMCSD_SUP_SDIO_IRQ (1 << 4) /* support signal pending SDIO IRQs */
-#define MMCSD_SUP_HIGHSPEED (1 << 5) /* support high speed */
-#define MMCSD_SUP_HIGHSPEED_DDR (1 << 6) /* support high speed(DDR) */
-#define MMCSD_SUP_HIGHSPEED_HS200 (1 << 7) /* support high speed HS200 */
-#define MMCSD_SUP_HIGHSPEED_HS400 (1 << 8) /* support high speed HS400 */
+#define MMCSD_SUP_HIGHSPEED (1 << 5) /* support high speed SDR*/
+#define MMCSD_SUP_DDR_3V3 (1 << 6)
+#define MMCSD_SUP_DDR_1V8 (1 << 7)
+#define MMCSD_SUP_DDR_1V2 (1 << 8)
+#define MMCSD_SUP_HIGHSPEED_DDR (MMCSD_SUP_DDR_3V3 | MMCSD_SUP_DDR_1V8 | MMCSD_SUP_DDR_1V2)/* HIGH SPEED DDR*/
+#define MMCSD_SUP_HS200_1V8 (1 << 9)
+#define MMCSD_SUP_HS200_1V2 (1 << 10)
+#define MMCSD_SUP_HS200 (MMCSD_SUP_HS200_1V2 | MMCSD_SUP_HS200_1V8) /* hs200 sdr */
+#define MMCSD_SUP_NONREMOVABLE (1 << 11)
rt_uint32_t max_seg_size; /* maximum size of one dma segment */
rt_uint32_t max_dma_segs; /* maximum number of dma segments in one request */
diff --git a/components/drivers/include/drivers/rt_dev_bus.h b/components/drivers/include/drivers/rt_dev_bus.h
new file mode 100644
index 0000000000..9fe8af893c
--- /dev/null
+++ b/components/drivers/include/drivers/rt_dev_bus.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-10-13 flybreak the first version
+ */
+
+#ifndef __RT_DEV_BUS_H__
+#define __RT_DEV_BUS_H__
+#include <rtthread.h>
+
+rt_device_t rt_device_bus_create(char *name, int attach_size);
+rt_err_t rt_device_bus_destroy(rt_device_t dev);
+
+#endif /* __RT_BUS_H__ */
diff --git a/components/drivers/include/drivers/serial.h b/components/drivers/include/drivers/serial.h
index 3b0d4c80da..1d0b9afe5c 100644
--- a/components/drivers/include/drivers/serial.h
+++ b/components/drivers/include/drivers/serial.h
@@ -150,6 +150,8 @@ struct rt_serial_device
void *serial_rx;
void *serial_tx;
+
+ struct rt_device_notify rx_notify;
};
typedef struct rt_serial_device rt_serial_t;
diff --git a/components/drivers/include/drivers/serial_v2.h b/components/drivers/include/drivers/serial_v2.h
index a449372e8a..b1020aaf99 100644
--- a/components/drivers/include/drivers/serial_v2.h
+++ b/components/drivers/include/drivers/serial_v2.h
@@ -158,6 +158,8 @@ struct rt_serial_device
void *serial_rx;
void *serial_tx;
+
+ struct rt_device_notify rx_notify;
};
/**
diff --git a/components/drivers/include/ipc/pipe.h b/components/drivers/include/ipc/pipe.h
index 08b9f266c8..5f64506011 100644
--- a/components/drivers/include/ipc/pipe.h
+++ b/components/drivers/include/ipc/pipe.h
@@ -20,16 +20,18 @@ struct rt_pipe_device
{
struct rt_device parent;
rt_bool_t is_named;
+#ifdef RT_USING_POSIX_DEVIO
+ int pipeno; /* for unamed pipe */
+#endif
/* ring buffer in pipe device */
struct rt_ringbuffer *fifo;
rt_uint16_t bufsz;
- rt_uint8_t readers;
- rt_uint8_t writers;
-
rt_wqueue_t reader_queue;
rt_wqueue_t writer_queue;
+ int writer;
+ int reader;
struct rt_mutex lock;
};
diff --git a/components/drivers/include/ipc/waitqueue.h b/components/drivers/include/ipc/waitqueue.h
index fbf5f93142..0727810696 100644
--- a/components/drivers/include/ipc/waitqueue.h
+++ b/components/drivers/include/ipc/waitqueue.h
@@ -43,6 +43,8 @@ rt_inline void rt_wqueue_init(rt_wqueue_t *queue)
void rt_wqueue_add(rt_wqueue_t *queue, struct rt_wqueue_node *node);
void rt_wqueue_remove(struct rt_wqueue_node *node);
int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int timeout);
+int rt_wqueue_wait_killable(rt_wqueue_t *queue, int condition, int timeout);
+int rt_wqueue_wait_interruptible(rt_wqueue_t *queue, int condition, int timeout);
void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key);
#define DEFINE_WAIT_FUNC(name, function) \
diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h
index 18c3ef9cd4..c7a61c1994 100644
--- a/components/drivers/include/rtdevice.h
+++ b/components/drivers/include/rtdevice.h
@@ -157,6 +157,10 @@ extern "C" {
#include "drivers/touch.h"
#endif
+#ifdef RT_USING_DEV_BUS
+#include "drivers/rt_dev_bus.h"
+#endif
+
#ifdef __cplusplus
}
#endif
diff --git a/components/drivers/ipc/completion.c b/components/drivers/ipc/completion.c
index 84dfb740b1..3f7644216b 100644
--- a/components/drivers/ipc/completion.c
+++ b/components/drivers/ipc/completion.c
@@ -80,11 +80,14 @@ rt_err_t rt_completion_wait(struct rt_completion *completion,
thread->error = RT_EOK;
/* suspend thread */
- rt_thread_suspend(thread);
+ rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE);
/* add to suspended list */
rt_list_insert_before(&(completion->suspended_list),
&(thread->tlist));
+ /* current context checking */
+ RT_DEBUG_NOT_IN_INTERRUPT;
+
/* start timer */
if (timeout > 0)
{
diff --git a/components/drivers/ipc/dataqueue.c b/components/drivers/ipc/dataqueue.c
index 4b1a14add9..6b53962384 100644
--- a/components/drivers/ipc/dataqueue.c
+++ b/components/drivers/ipc/dataqueue.c
@@ -119,7 +119,7 @@ rt_err_t rt_data_queue_push(struct rt_data_queue *queue,
thread->error = RT_EOK;
/* suspend thread on the push list */
- rt_thread_suspend(thread);
+ rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE);
rt_list_insert_before(&(queue->suspended_push_list), &(thread->tlist));
/* start timer */
if (timeout > 0)
@@ -237,7 +237,7 @@ rt_err_t rt_data_queue_pop(struct rt_data_queue *queue,
thread->error = RT_EOK;
/* suspend thread on the pop list */
- rt_thread_suspend(thread);
+ rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE);
rt_list_insert_before(&(queue->suspended_pop_list), &(thread->tlist));
/* start timer */
if (timeout > 0)
diff --git a/components/drivers/ipc/pipe.c b/components/drivers/ipc/pipe.c
index 5483ffa549..7567a73482 100644
--- a/components/drivers/ipc/pipe.c
+++ b/components/drivers/ipc/pipe.c
@@ -19,6 +19,28 @@
#include <poll.h>
#include <sys/ioctl.h>
#include <dfs_file.h>
+#include <resource_id.h>
+
+/* check RT_UNAMED_PIPE_NUMBER */
+
+#ifndef RT_UNAMED_PIPE_NUMBER
+#define RT_UNAMED_PIPE_NUMBER 64
+#endif
+
+#define BITS(x) _BITS(x)
+#define _BITS(x) (sizeof(#x) - 1)
+
+struct check_rt_unamed_pipe_number
+{
+ /* -4 for "pipe" prefix */
+ /* -1 for '\0' postfix */
+ char _check[RT_NAME_MAX - 4 - 1 - BITS(RT_UNAMED_PIPE_NUMBER)];
+};
+
+/* check end */
+
+static void *resoure_id[RT_UNAMED_PIPE_NUMBER];
+static resource_id_t id_mgr = RESOURCE_ID_INIT(RT_UNAMED_PIPE_NUMBER, resoure_id);
/**
* @brief This function will open a pipe.
@@ -33,16 +55,26 @@
static int pipe_fops_open(struct dfs_fd *fd)
{
int rc = 0;
- rt_device_t device;
rt_pipe_t *pipe;
- pipe = (rt_pipe_t *)fd->data;
- if (!pipe) return -1;
+ pipe = (rt_pipe_t *)fd->vnode->data;
+ if (!pipe)
+ {
+ return -1;
+ }
+
+ rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER);
- device = &(pipe->parent);
- rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER);
+ if ((fd->flags & O_RDONLY) == O_RDONLY)
+ {
+ pipe->reader = 1;
+ }
- if (device->ref_count == 0)
+ if ((fd->flags & O_WRONLY) == O_WRONLY)
+ {
+ pipe->writer = 1;
+ }
+ if (fd->vnode->ref_count == 1)
{
pipe->fifo = rt_ringbuffer_create(pipe->bufsz);
if (pipe->fifo == RT_NULL)
@@ -52,23 +84,8 @@ static int pipe_fops_open(struct dfs_fd *fd)
}
}
- switch (fd->flags & O_ACCMODE)
- {
- case O_RDONLY:
- pipe->readers ++;
- break;
- case O_WRONLY:
- pipe->writers ++;
- break;
- case O_RDWR:
- pipe->readers ++;
- pipe->writers ++;
- break;
- }
- device->ref_count ++;
-
__exit:
- rt_mutex_release(&(pipe->lock));
+ rt_mutex_release(&pipe->lock);
return rc;
}
@@ -87,47 +104,41 @@ static int pipe_fops_close(struct dfs_fd *fd)
rt_device_t device;
rt_pipe_t *pipe;
- pipe = (rt_pipe_t *)fd->data;
- if (!pipe) return -1;
-
- device = &(pipe->parent);
- rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER);
-
- switch (fd->flags & O_ACCMODE)
+ pipe = (rt_pipe_t *)fd->vnode->data;
+ if (!pipe)
{
- case O_RDONLY:
- pipe->readers --;
- break;
- case O_WRONLY:
- pipe->writers --;
- break;
- case O_RDWR:
- pipe->readers --;
- pipe->writers --;
- break;
+ return -1;
}
- if (pipe->writers == 0)
+ device = &pipe->parent;
+ rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER);
+
+ if ((fd->flags & O_RDONLY) == O_RDONLY)
{
- rt_wqueue_wakeup(&(pipe->reader_queue), (void *)(POLLIN | POLLERR | POLLHUP));
+ pipe->reader = 0;
}
- if (pipe->readers == 0)
+ if ((fd->flags & O_WRONLY) == O_WRONLY)
{
- rt_wqueue_wakeup(&(pipe->writer_queue), (void *)(POLLOUT | POLLERR | POLLHUP));
+ pipe->writer = 0;
+ while (!rt_list_isempty(&pipe->reader_queue.waiting_list))
+ {
+ rt_wqueue_wakeup(&pipe->reader_queue, (void*)POLLIN);
+ }
}
- if (device->ref_count == 1)
+ if (fd->vnode->ref_count == 1)
{
if (pipe->fifo != RT_NULL)
+ {
rt_ringbuffer_destroy(pipe->fifo);
+ }
pipe->fifo = RT_NULL;
}
- device->ref_count --;
- rt_mutex_release(&(pipe->lock));
+ rt_mutex_release(&pipe->lock);
- if (device->ref_count == 0 && pipe->is_named == RT_FALSE)
+ if (fd->vnode->ref_count == 1 && pipe->is_named == RT_FALSE)
{
/* delete the unamed pipe */
rt_pipe_delete(device->parent.name);
@@ -158,15 +169,15 @@ static int pipe_fops_ioctl(struct dfs_fd *fd, int cmd, void *args)
rt_pipe_t *pipe;
int ret = 0;
- pipe = (rt_pipe_t *)fd->data;
+ pipe = (rt_pipe_t *)fd->vnode->data;
switch (cmd)
{
case FIONREAD:
- *((int *)args) = rt_ringbuffer_data_len(pipe->fifo);
+ *((int*)args) = rt_ringbuffer_data_len(pipe->fifo);
break;
case FIONWRITE:
- *((int *)args) = rt_ringbuffer_space_len(pipe->fifo);
+ *((int*)args) = rt_ringbuffer_space_len(pipe->fifo);
break;
default:
ret = -EINVAL;
@@ -194,24 +205,16 @@ static int pipe_fops_read(struct dfs_fd *fd, void *buf, size_t count)
int len = 0;
rt_pipe_t *pipe;
- pipe = (rt_pipe_t *)fd->data;
+ pipe = (rt_pipe_t *)fd->vnode->data;
/* no process has the pipe open for writing, return end-of-file */
- if (pipe->writers == 0)
- return 0;
-
- rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER);
+ rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER);
while (1)
{
- if (pipe->writers == 0)
- {
- goto out;
- }
-
len = rt_ringbuffer_get(pipe->fifo, buf, count);
- if (len > 0)
+ if (len > 0 || pipe->writer == 0)
{
break;
}
@@ -224,14 +227,14 @@ static int pipe_fops_read(struct dfs_fd *fd, void *buf, size_t count)
}
rt_mutex_release(&pipe->lock);
- rt_wqueue_wakeup(&(pipe->writer_queue), (void *)POLLOUT);
- rt_wqueue_wait(&(pipe->reader_queue), 0, -1);
- rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER);
+ rt_wqueue_wakeup(&pipe->writer_queue, (void*)POLLOUT);
+ rt_wqueue_wait(&pipe->reader_queue, 0, -1);
+ rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER);
}
}
/* wakeup writer */
- rt_wqueue_wakeup(&(pipe->writer_queue), (void *)POLLOUT);
+ rt_wqueue_wakeup(&pipe->writer_queue, (void*)POLLOUT);
out:
rt_mutex_release(&pipe->lock);
@@ -259,29 +262,18 @@ static int pipe_fops_write(struct dfs_fd *fd, const void *buf, size_t count)
int ret = 0;
uint8_t *pbuf;
- pipe = (rt_pipe_t *)fd->data;
-
- if (pipe->readers == 0)
- {
- ret = -EPIPE;
- goto out;
- }
+ pipe = (rt_pipe_t *)fd->vnode->data;
if (count == 0)
+ {
return 0;
+ }
- pbuf = (uint8_t *)buf;
+ pbuf = (uint8_t*)buf;
rt_mutex_take(&pipe->lock, -1);
while (1)
{
- if (pipe->readers == 0)
- {
- if (ret == 0)
- ret = -EPIPE;
- break;
- }
-
len = rt_ringbuffer_put(pipe->fifo, pbuf, count - ret);
ret += len;
pbuf += len;
@@ -305,19 +297,18 @@ static int pipe_fops_write(struct dfs_fd *fd, const void *buf, size_t count)
}
rt_mutex_release(&pipe->lock);
- rt_wqueue_wakeup(&(pipe->reader_queue), (void *)POLLIN);
+ rt_wqueue_wakeup(&pipe->reader_queue, (void*)POLLIN);
/* pipe full, waiting on suspended write list */
- rt_wqueue_wait(&(pipe->writer_queue), 0, -1);
+ rt_wqueue_wait(&pipe->writer_queue, 0, -1);
rt_mutex_take(&pipe->lock, -1);
}
rt_mutex_release(&pipe->lock);
if (wakeup)
{
- rt_wqueue_wakeup(&(pipe->reader_queue), (void *)POLLIN);
+ rt_wqueue_wakeup(&pipe->reader_queue, (void*)POLLIN);
}
-out:
return ret;
}
@@ -339,10 +330,10 @@ static int pipe_fops_poll(struct dfs_fd *fd, rt_pollreq_t *req)
int mask = 0;
rt_pipe_t *pipe;
int mode = 0;
- pipe = (rt_pipe_t *)fd->data;
+ pipe = (rt_pipe_t *)fd->vnode->data;
- rt_poll_add(&(pipe->reader_queue), req);
- rt_poll_add(&(pipe->writer_queue), req);
+ rt_poll_add(&pipe->reader_queue, req);
+ rt_poll_add(&pipe->writer_queue, req);
switch (fd->flags & O_ACCMODE)
{
@@ -363,10 +354,6 @@ static int pipe_fops_poll(struct dfs_fd *fd, rt_pollreq_t *req)
{
mask |= POLLIN;
}
- if (pipe->writers == 0)
- {
- mask |= POLLHUP;
- }
}
if (mode & 2)
@@ -375,10 +362,6 @@ static int pipe_fops_poll(struct dfs_fd *fd, rt_pollreq_t *req)
{
mask |= POLLOUT;
}
- if (pipe->readers == 0)
- {
- mask |= POLLERR;
- }
}
return mask;
@@ -410,7 +393,7 @@ static const struct dfs_file_ops pipe_fops =
* When the return value is -RT_EINVAL, it means the device handle is empty.
* When the return value is -RT_ENOMEM, it means insufficient memory allocation failed.
*/
-static rt_err_t rt_pipe_open(rt_device_t device, rt_uint16_t oflag)
+rt_err_t rt_pipe_open(rt_device_t device, rt_uint16_t oflag)
{
rt_pipe_t *pipe = (rt_pipe_t *)device;
rt_err_t ret = RT_EOK;
@@ -421,7 +404,7 @@ static rt_err_t rt_pipe_open(rt_device_t device, rt_uint16_t oflag)
goto __exit;
}
- rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER);
+ rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER);
if (pipe->fifo == RT_NULL)
{
@@ -432,7 +415,7 @@ static rt_err_t rt_pipe_open(rt_device_t device, rt_uint16_t oflag)
}
}
- rt_mutex_release(&(pipe->lock));
+ rt_mutex_release(&pipe->lock);
__exit:
return ret;
@@ -447,20 +430,20 @@ __exit:
* When the return value is RT_EOK, the operation is successful.
* When the return value is -RT_EINVAL, it means the device handle is empty.
*/
-static rt_err_t rt_pipe_close(rt_device_t device)
+rt_err_t rt_pipe_close(rt_device_t device)
{
rt_pipe_t *pipe = (rt_pipe_t *)device;
- if (device == RT_NULL) return -RT_EINVAL;
- rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER);
-
- if (device->ref_count == 1)
+ if (device == RT_NULL)
{
- rt_ringbuffer_destroy(pipe->fifo);
- pipe->fifo = RT_NULL;
+ return -RT_EINVAL;
}
+ rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER);
+
+ rt_ringbuffer_destroy(pipe->fifo);
+ pipe->fifo = RT_NULL;
- rt_mutex_release(&(pipe->lock));
+ rt_mutex_release(&pipe->lock);
return RT_EOK;
}
@@ -479,7 +462,7 @@ static rt_err_t rt_pipe_close(rt_device_t device)
* @return Return the length of data read.
* When the return value is 0, it means the pipe device handle is empty or the count is 0.
*/
-static rt_size_t rt_pipe_read(rt_device_t device, rt_off_t pos, void *buffer, rt_size_t count)
+rt_size_t rt_pipe_read(rt_device_t device, rt_off_t pos, void *buffer, rt_size_t count)
{
uint8_t *pbuf;
rt_size_t read_bytes = 0;
@@ -487,18 +470,24 @@ static rt_size_t rt_pipe_read(rt_device_t device, rt_off_t pos, void *buffer, rt
if (device == RT_NULL)
{
- rt_set_errno(EINVAL);
+ rt_set_errno(-EINVAL);
+ return 0;
+ }
+ if (count == 0)
+ {
return 0;
}
- if (count == 0) return 0;
- pbuf = (uint8_t *)buffer;
- rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER);
+ pbuf = (uint8_t*)buffer;
+ rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER);
while (read_bytes < count)
{
- int len = rt_ringbuffer_get(pipe->fifo, &pbuf[read_bytes], (rt_uint16_t)(count - read_bytes));
- if (len <= 0) break;
+ int len = rt_ringbuffer_get(pipe->fifo, &pbuf[read_bytes], count - read_bytes);
+ if (len <= 0)
+ {
+ break;
+ }
read_bytes += len;
}
@@ -521,7 +510,7 @@ static rt_size_t rt_pipe_read(rt_device_t device, rt_off_t pos, void *buffer, rt
* @return Return the length of data written.
* When the return value is 0, it means the pipe device handle is empty or the count is 0.
*/
-static rt_size_t rt_pipe_write(rt_device_t device, rt_off_t pos, const void *buffer, rt_size_t count)
+rt_size_t rt_pipe_write(rt_device_t device, rt_off_t pos, const void *buffer, rt_size_t count)
{
uint8_t *pbuf;
rt_size_t write_bytes = 0;
@@ -529,18 +518,24 @@ static rt_size_t rt_pipe_write(rt_device_t device, rt_off_t pos, const void *buf
if (device == RT_NULL)
{
- rt_set_errno(EINVAL);
+ rt_set_errno(-EINVAL);
+ return 0;
+ }
+ if (count == 0)
+ {
return 0;
}
- if (count == 0) return 0;
- pbuf = (uint8_t *)buffer;
+ pbuf = (uint8_t*)buffer;
rt_mutex_take(&pipe->lock, -1);
while (write_bytes < count)
{
- int len = rt_ringbuffer_put(pipe->fifo, &pbuf[write_bytes], (rt_uint16_t)(count - write_bytes));
- if (len <= 0) break;
+ int len = rt_ringbuffer_put(pipe->fifo, &pbuf[write_bytes], count - write_bytes);
+ if (len <= 0)
+ {
+ break;
+ }
write_bytes += len;
}
@@ -560,7 +555,7 @@ static rt_size_t rt_pipe_write(rt_device_t device, rt_off_t pos, const void *buf
*
* @return Always return RT_EOK.
*/
-static rt_err_t rt_pipe_control(rt_device_t dev, int cmd, void *args)
+rt_err_t rt_pipe_control(rt_device_t dev, int cmd, void *args)
{
return RT_EOK;
}
@@ -599,14 +594,19 @@ rt_pipe_t *rt_pipe_create(const char *name, int bufsz)
rt_memset(pipe, 0, sizeof(rt_pipe_t));
pipe->is_named = RT_TRUE; /* initialize as a named pipe */
- rt_mutex_init(&(pipe->lock), name, RT_IPC_FLAG_PRIO);
- rt_wqueue_init(&(pipe->reader_queue));
- rt_wqueue_init(&(pipe->writer_queue));
+#if defined(RT_USING_POSIX_DEVIO) && defined(RT_USING_POSIX_PIPE)
+ pipe->pipeno = -1;
+#endif
+ rt_mutex_init(&pipe->lock, name, RT_IPC_FLAG_FIFO);
+ rt_wqueue_init(&pipe->reader_queue);
+ rt_wqueue_init(&pipe->writer_queue);
+ pipe->writer = 0;
+ pipe->reader = 0;
RT_ASSERT(bufsz < 0xFFFF);
pipe->bufsz = bufsz;
- dev = &(pipe->parent);
+ dev = &pipe->parent;
dev->type = RT_Device_Class_Pipe;
#ifdef RT_USING_DEVICE_OPS
dev->ops = &pipe_ops;
@@ -622,8 +622,12 @@ rt_pipe_t *rt_pipe_create(const char *name, int bufsz)
dev->rx_indicate = RT_NULL;
dev->tx_complete = RT_NULL;
- if (rt_device_register(&(pipe->parent), name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE) != 0)
+ if (rt_device_register(&pipe->parent, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE) != 0)
{
+ rt_mutex_detach(&pipe->lock);
+#if defined(RT_USING_POSIX_DEVIO) && defined(RT_USING_POSIX_PIPE)
+ resource_id_put(&id_mgr, pipe->pipeno);
+#endif
rt_free(pipe);
return RT_NULL;
}
@@ -657,14 +661,12 @@ int rt_pipe_delete(const char *name)
{
rt_pipe_t *pipe;
- if (device->ref_count != 0)
- {
- return -RT_EBUSY;
- }
-
pipe = (rt_pipe_t *)device;
- rt_mutex_detach(&(pipe->lock));
+ rt_mutex_detach(&pipe->lock);
+#if defined(RT_USING_POSIX_DEVIO) && defined(RT_USING_POSIX_PIPE)
+ resource_id_put(&id_mgr, pipe->pipeno);
+#endif
rt_device_unregister(device);
/* close fifo ringbuffer */
@@ -677,12 +679,12 @@ int rt_pipe_delete(const char *name)
}
else
{
- result = -RT_EINVAL;
+ result = -ENODEV;
}
}
else
{
- result = -RT_EINVAL;
+ result = -ENODEV;
}
return result;
@@ -702,19 +704,26 @@ int rt_pipe_delete(const char *name)
int pipe(int fildes[2])
{
rt_pipe_t *pipe;
- char dname[RT_NAME_MAX];
- char dev_name[RT_NAME_MAX * 4];
- static int pipeno = 0;
+ char dname[8];
+ char dev_name[32];
+ int pipeno = 0;
- rt_snprintf(dname, sizeof(dname), "pipe%d", pipeno++);
+ pipeno = resource_id_get(&id_mgr);
+ if (pipeno == -1)
+ {
+ return -1;
+ }
+ rt_snprintf(dname, sizeof(dname), "pipe%d", pipeno);
pipe = rt_pipe_create(dname, RT_USING_POSIX_PIPE_SIZE);
if (pipe == RT_NULL)
{
+ resource_id_put(&id_mgr, pipeno);
return -1;
}
pipe->is_named = RT_FALSE; /* unamed pipe */
+ pipe->pipeno = pipeno;
rt_snprintf(dev_name, sizeof(dev_name), "/dev/%s", dname);
fildes[0] = open(dev_name, O_RDONLY, 0);
if (fildes[0] < 0)
diff --git a/components/drivers/ipc/waitqueue.c b/components/drivers/ipc/waitqueue.c
index 97b5075638..25ebc71f68 100644
--- a/components/drivers/ipc/waitqueue.c
+++ b/components/drivers/ipc/waitqueue.c
@@ -118,13 +118,14 @@ void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key)
*
* @return Return 0 if the thread is woken up.
*/
-int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec)
+static int _rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec, int suspend_flag)
{
int tick;
rt_thread_t tid = rt_thread_self();
rt_timer_t tmr = &(tid->thread_timer);
struct rt_wqueue_node __wait;
rt_base_t level;
+ rt_err_t ret;
/* current context checking */
RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE);
@@ -150,8 +151,14 @@ int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec)
goto __exit_wakeup;
}
+ ret = rt_thread_suspend_with_flag(tid, suspend_flag);
+ if (ret != RT_EOK)
+ {
+ rt_hw_interrupt_enable(level);
+ /* suspend failed */
+ return -RT_EINTR;
+ }
rt_wqueue_add(queue, &__wait);
- rt_thread_suspend(tid);
/* start timer */
if (tick != RT_WAITING_FOREVER)
@@ -176,3 +183,18 @@ __exit_wakeup:
return tid->error;
}
+
+int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec)
+{
+ return _rt_wqueue_wait(queue, condition, msec, RT_UNINTERRUPTIBLE);
+}
+
+int rt_wqueue_wait_killable(rt_wqueue_t *queue, int condition, int msec)
+{
+ return _rt_wqueue_wait(queue, condition, msec, RT_KILLABLE);
+}
+
+int rt_wqueue_wait_interruptible(rt_wqueue_t *queue, int condition, int msec)
+{
+ return _rt_wqueue_wait(queue, condition, msec, RT_INTERRUPTIBLE);
+}
diff --git a/components/drivers/ipc/workqueue.c b/components/drivers/ipc/workqueue.c
index 3dae192d0d..d9b98929c8 100644
--- a/components/drivers/ipc/workqueue.c
+++ b/components/drivers/ipc/workqueue.c
@@ -65,7 +65,7 @@ static void _workqueue_thread_entry(void *parameter)
if (rt_list_isempty(&(queue->work_list)))
{
/* no software timer exist, suspend self. */
- rt_thread_suspend(rt_thread_self());
+ rt_thread_suspend_with_flag(rt_thread_self(), RT_UNINTERRUPTIBLE);
rt_hw_interrupt_enable(level);
rt_schedule();
continue;
@@ -116,7 +116,7 @@ static rt_err_t _workqueue_submit_work(struct rt_workqueue *queue,
/* whether the workqueue is doing work */
if (queue->work_current == RT_NULL &&
- ((queue->work_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND))
+ ((queue->work_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_STAT_MASK))
{
/* resume work thread */
rt_thread_resume(queue->work_thread);
@@ -198,7 +198,7 @@ static void _delayed_work_timeout_handler(void *parameter)
}
/* whether the workqueue is doing work */
if (queue->work_current == RT_NULL &&
- ((queue->work_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND))
+ ((queue->work_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_STAT_MASK))
{
/* resume work thread */
rt_thread_resume(queue->work_thread);
@@ -356,7 +356,7 @@ rt_err_t rt_workqueue_urgent_work(struct rt_workqueue *queue, struct rt_work *wo
rt_list_insert_after(&queue->work_list, &(work->list));
/* whether the workqueue is doing work */
if (queue->work_current == RT_NULL &&
- ((queue->work_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND))
+ ((queue->work_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_STAT_MASK))
{
/* resume work thread */
rt_thread_resume(queue->work_thread);
diff --git a/components/drivers/misc/SConscript b/components/drivers/misc/SConscript
index 72058eeeca..b0d0354cf4 100644
--- a/components/drivers/misc/SConscript
+++ b/components/drivers/misc/SConscript
@@ -23,6 +23,18 @@ if GetDepend(['RT_USING_PULSE_ENCODER']):
if GetDepend(['RT_USING_INPUT_CAPTURE']):
src = src + ['rt_inputcapture.c']
+if GetDepend(['RT_USING_DEV_BUS']):
+ src = src + ['rt_dev_bus.c']
+
+if GetDepend(['RT_USING_NULL']):
+ src = src + ['rt_null.c']
+
+if GetDepend(['RT_USING_ZERO']):
+ src = src + ['rt_zero.c']
+
+if GetDepend(['RT_USING_RANDOM']):
+ src = src + ['rt_random.c']
+
if len(src):
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
diff --git a/components/drivers/misc/rt_dev_bus.c b/components/drivers/misc/rt_dev_bus.c
new file mode 100644
index 0000000000..5e858d1781
--- /dev/null
+++ b/components/drivers/misc/rt_dev_bus.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-10-13 flybreak the first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#define DBG_TAG "dev_bus"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#if defined(RT_USING_POSIX_DEVIO)
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <dfs_file.h>
+
+static int bus_fops_open(struct dfs_fd *fd)
+{
+ LOG_D("bus fops open");
+ return 0;
+}
+
+static int bus_fops_close(struct dfs_fd *fd)
+{
+ LOG_D("bus fops close");
+ return 0;
+}
+
+static const struct dfs_file_ops bus_fops =
+{
+ bus_fops_open,
+ bus_fops_close,
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+};
+#endif
+
+rt_device_t rt_device_bus_create(char *name, int attach_size)
+{
+ rt_err_t result = RT_EOK;
+ rt_device_t dev = rt_device_create(RT_Device_Class_Bus, 0);
+
+ result = rt_device_register(dev, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE);
+ if (result < 0)
+ {
+ rt_kprintf("dev bus [%s] register failed!, ret=%d\n", name, result);
+ return RT_NULL;
+ }
+#if defined(RT_USING_POSIX_DEVIO)
+ dev->fops = &bus_fops;
+#endif
+
+ LOG_D("bus create");
+ return dev;
+}
+
+rt_err_t rt_device_bus_destroy(rt_device_t dev)
+{
+ rt_device_unregister(dev);
+ dev->parent.type = RT_Object_Class_Device;
+ rt_device_destroy(dev);
+ LOG_D("bus destroy");
+ return RT_EOK;
+}
diff --git a/components/drivers/misc/rt_null.c b/components/drivers/misc/rt_null.c
new file mode 100644
index 0000000000..3e4cf6f3da
--- /dev/null
+++ b/components/drivers/misc/rt_null.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2011-2020, Shanghai Real-Thread Electronic Technology Co.,Ltd
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-12-03 quanzhao the first version
+ */
+
+#include <time.h>
+#include <string.h>
+#include <rtthread.h>
+
+static struct rt_device null_dev;
+
+static rt_size_t null_read (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+ return 0;
+}
+
+static rt_size_t null_write (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+ return size;
+}
+
+static rt_err_t null_control (rt_device_t dev, int cmd, void *args)
+{
+ return RT_EOK;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops null_ops =
+{
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ null_read,
+ null_write,
+ null_control
+};
+#endif
+
+int null_device_init(void)
+{
+ static rt_bool_t init_ok = RT_FALSE;
+
+ if (init_ok)
+ {
+ return 0;
+ }
+ RT_ASSERT(!rt_device_find("null"));
+ null_dev.type = RT_Device_Class_Miscellaneous;
+
+#ifdef RT_USING_DEVICE_OPS
+ null_dev.ops = &null_ops;
+#else
+ null_dev.init = RT_NULL;
+ null_dev.open = RT_NULL;
+ null_dev.close = RT_NULL;
+ null_dev.read = null_read;
+ null_dev.write = null_write;
+ null_dev.control = null_control;
+#endif
+
+ /* no private */
+ null_dev.user_data = RT_NULL;
+
+ rt_device_register(&null_dev, "null", RT_DEVICE_FLAG_RDWR);
+
+ init_ok = RT_TRUE;
+
+ return 0;
+}
+INIT_DEVICE_EXPORT(null_device_init);
+
diff --git a/components/drivers/misc/rt_random.c b/components/drivers/misc/rt_random.c
new file mode 100644
index 0000000000..1ea0480b7c
--- /dev/null
+++ b/components/drivers/misc/rt_random.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2011-2020, Shanghai Real-Thread Electronic Technology Co.,Ltd
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-12-18 quanzhao the first version
+ */
+
+#include <time.h>
+#include <string.h>
+#include <rtthread.h>
+
+static struct rt_device random_dev;
+static unsigned long seed;
+
+static rt_uint16_t calc_random(void)
+{
+ seed = 214013L * seed + 2531011L;
+ return (seed >> 16) & 0x7FFF; /* return bits 16~30 */
+}
+
+static rt_size_t random_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+ rt_uint16_t rand = calc_random();
+ ssize_t ret = sizeof(rand);
+ rt_memcpy(buffer, &rand, ret);
+ return ret;
+}
+
+static rt_size_t random_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+ ssize_t ret = sizeof(seed);
+ rt_memcpy(&seed, buffer, ret);
+ return ret;
+}
+
+static rt_err_t random_control(rt_device_t dev, int cmd, void *args)
+{
+ return RT_EOK;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops random_ops =
+{
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ random_read,
+ random_write,
+ random_control
+};
+#endif
+
+int random_device_init(void)
+{
+ static rt_bool_t init_ok = RT_FALSE;
+
+ if (init_ok)
+ {
+ return 0;
+ }
+ RT_ASSERT(!rt_device_find("random"));
+ random_dev.type = RT_Device_Class_Miscellaneous;
+
+#ifdef RT_USING_DEVICE_OPS
+ random_dev.ops = &random_ops;
+#else
+ random_dev.init = RT_NULL;
+ random_dev.open = RT_NULL;
+ random_dev.close = RT_NULL;
+ random_dev.read = random_read;
+ random_dev.write = random_write;
+ random_dev.control = random_control;
+#endif
+
+ /* no private */
+ random_dev.user_data = RT_NULL;
+
+ rt_device_register(&random_dev, "random", RT_DEVICE_FLAG_RDWR);
+
+ init_ok = RT_TRUE;
+
+ return 0;
+}
+INIT_DEVICE_EXPORT(random_device_init);
+
+static struct rt_device urandom_dev;
+static unsigned long useed;
+
+static rt_uint16_t calc_urandom(void)
+{
+ useed = 214013L * useed + 2531011L;
+ return (useed >> 16) & 0x7FFF; /* return bits 16~30 */
+}
+
+static rt_size_t random_uread(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+ rt_uint16_t rand = calc_urandom();
+ ssize_t ret = sizeof(rand);
+ rt_memcpy(buffer, &rand, ret);
+ return ret;
+}
+
+static rt_size_t random_uwrite(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+ ssize_t ret = sizeof(useed);
+ rt_memcpy(&useed, buffer, ret);
+ return ret;
+}
+
+static rt_err_t random_ucontrol(rt_device_t dev, int cmd, void *args)
+{
+ return RT_EOK;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops urandom_ops =
+{
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ random_uread,
+ random_uwrite,
+ random_ucontrol
+};
+#endif
+
+int urandom_device_init(void)
+{
+ static rt_bool_t init_ok = RT_FALSE;
+
+ if (init_ok)
+ {
+ return 0;
+ }
+ RT_ASSERT(!rt_device_find("urandom"));
+ urandom_dev.type = RT_Device_Class_Miscellaneous;
+
+#ifdef RT_USING_DEVICE_OPS
+ urandom_dev.ops = &urandom_ops;
+#else
+ urandom_dev.init = RT_NULL;
+ urandom_dev.open = RT_NULL;
+ urandom_dev.close = RT_NULL;
+ urandom_dev.read = random_uread;
+ urandom_dev.write = random_uwrite;
+ urandom_dev.control = random_ucontrol;
+#endif
+
+ /* no private */
+ urandom_dev.user_data = RT_NULL;
+
+ rt_device_register(&urandom_dev, "urandom", RT_DEVICE_FLAG_RDWR);
+
+ init_ok = RT_TRUE;
+
+ return 0;
+}
+INIT_DEVICE_EXPORT(urandom_device_init);
diff --git a/components/drivers/misc/rt_zero.c b/components/drivers/misc/rt_zero.c
new file mode 100644
index 0000000000..f2657a2360
--- /dev/null
+++ b/components/drivers/misc/rt_zero.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2011-2020, Shanghai Real-Thread Electronic Technology Co.,Ltd
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2020-12-03 quanzhao the first version
+ */
+
+#include <time.h>
+#include <string.h>
+#include <rtthread.h>
+
+static struct rt_device zero_dev;
+
+static rt_size_t zero_read (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+ rt_memset(buffer, 0, size);
+ return size;
+}
+
+static rt_size_t zero_write (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+ return size;
+}
+
+static rt_err_t zero_control (rt_device_t dev, int cmd, void *args)
+{
+ return RT_EOK;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops zero_ops =
+{
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ zero_read,
+ zero_write,
+ zero_control
+};
+#endif
+
+int zero_device_init(void)
+{
+ static rt_bool_t init_ok = RT_FALSE;
+
+ if (init_ok)
+ {
+ return 0;
+ }
+ RT_ASSERT(!rt_device_find("zero"));
+ zero_dev.type = RT_Device_Class_Miscellaneous;
+
+#ifdef RT_USING_DEVICE_OPS
+ zero_dev.ops = &zero_ops;
+#else
+ zero_dev.init = RT_NULL;
+ zero_dev.open = RT_NULL;
+ zero_dev.close = RT_NULL;
+ zero_dev.read = zero_read;
+ zero_dev.write = zero_write;
+ zero_dev.control = zero_control;
+#endif
+
+ /* no private */
+ zero_dev.user_data = RT_NULL;
+
+ rt_device_register(&zero_dev, "zero", RT_DEVICE_FLAG_RDWR);
+
+ init_ok = RT_TRUE;
+
+ return 0;
+}
+INIT_DEVICE_EXPORT(zero_device_init);
+
diff --git a/components/drivers/sdio/SConscript b/components/drivers/sdio/SConscript
index 95dcfa2578..d5b6eb1cb9 100644
--- a/components/drivers/sdio/SConscript
+++ b/components/drivers/sdio/SConscript
@@ -7,6 +7,7 @@ block_dev.c
mmcsd_core.c
sd.c
sdio.c
+gpt.c
mmc.c
""")
diff --git a/components/drivers/sdio/block_dev.c b/components/drivers/sdio/block_dev.c
index 522fa753db..70159af30c 100644
--- a/components/drivers/sdio/block_dev.c
+++ b/components/drivers/sdio/block_dev.c
@@ -12,6 +12,7 @@
#include <dfs_fs.h>
#include <drivers/mmcsd_core.h>
+#include <drivers/gpt.h>
#define DBG_TAG "SDIO"
#ifdef RT_SDIO_DEBUG
@@ -21,6 +22,7 @@
#endif /* RT_SDIO_DEBUG */
#include <rtdbg.h>
+static rt_list_t blk_devices = RT_LIST_OBJECT_INIT(blk_devices);
#define BLK_MIN(a, b) ((a) < (b) ? (a) : (b))
@@ -37,6 +39,67 @@ struct mmcsd_blk_device
#ifndef RT_MMCSD_MAX_PARTITION
#define RT_MMCSD_MAX_PARTITION 16
#endif
+#define RT_GPT_PARTITION_MAX 128
+
+static int __send_status(struct rt_mmcsd_card *card, rt_uint32_t *status, unsigned retries)
+{
+ int err;
+ struct rt_mmcsd_cmd cmd;
+
+ cmd.busy_timeout = 0;
+ cmd.cmd_code = SEND_STATUS;
+ cmd.arg = card->rca << 16;
+ cmd.flags = RESP_R1 | CMD_AC;
+ err = mmcsd_send_cmd(card->host, &cmd, retries);
+ if (err)
+ return err;
+
+ if (status)
+ *status = cmd.resp[0];
+
+ return 0;
+}
+
+static int card_busy_detect(struct rt_mmcsd_card *card, unsigned int timeout_ms,
+ rt_uint32_t *resp_errs)
+{
+ int timeout = rt_tick_from_millisecond(timeout_ms);
+ int err = 0;
+ rt_uint32_t status;
+ rt_tick_t start;
+
+ start = rt_tick_get();
+ do
+ {
+ rt_bool_t out = (int)(rt_tick_get() - start) > timeout;
+
+ err = __send_status(card, &status, 5);
+ if (err)
+ {
+ LOG_E("error %d requesting status", err);
+ return err;
+ }
+
+ /* Accumulate any response error bits seen */
+ if (resp_errs)
+ *resp_errs |= status;
+
+ if (out)
+ {
+ LOG_E("wait card busy timeout");
+ return -RT_ETIMEOUT;
+ }
+ /*
+ * Some cards mishandle the status bits,
+ * so make sure to check both the busy
+ * indication and the card state.
+ */
+ }
+ while (!(status & R1_READY_FOR_DATA) ||
+ (R1_CURRENT_STATE(status) == 7));
+
+ return err;
+}
rt_int32_t mmcsd_num_wr_blocks(struct rt_mmcsd_card *card)
{
@@ -73,7 +136,7 @@ rt_int32_t mmcsd_num_wr_blocks(struct rt_mmcsd_card *card)
timeout_us = data.timeout_ns / 1000;
timeout_us += data.timeout_clks * 1000 /
- (card->host->io_cfg.clock / 1000);
+ (card->host->io_cfg.clock / 1000);
if (timeout_us > 100000)
{
@@ -148,44 +211,29 @@ static rt_err_t rt_mmcsd_req_blk(struct rt_mmcsd_card *card,
w_cmd = WRITE_BLOCK;
}
+ if (!controller_is_spi(card->host) && (card->flags & 0x8000))
+ {
+ /* last request is WRITE,need check busy */
+ card_busy_detect(card, 10000, RT_NULL);
+ }
+
if (!dir)
{
cmd.cmd_code = r_cmd;
data.flags |= DATA_DIR_READ;
+ card->flags &= 0x7fff;
}
else
{
cmd.cmd_code = w_cmd;
data.flags |= DATA_DIR_WRITE;
+ card->flags |= 0x8000;
}
mmcsd_set_data_timeout(&data, card);
data.buf = buf;
- mmcsd_send_request(host, &req);
- if (!controller_is_spi(card->host) && dir != 0)
- {
- do
- {
- rt_int32_t err;
-
- cmd.cmd_code = SEND_STATUS;
- cmd.arg = card->rca << 16;
- cmd.flags = RESP_R1 | CMD_AC;
- err = mmcsd_send_cmd(card->host, &cmd, 5);
- if (err)
- {
- LOG_E("error %d requesting status", err);
- break;
- }
- /*
- * Some cards mishandle the status bits,
- * so make sure to check both the busy
- * indication and the card state.
- */
- } while (!(cmd.resp[0] & R1_READY_FOR_DATA) ||
- (R1_CURRENT_STATE(cmd.resp[0]) == 7));
- }
+ mmcsd_send_request(host, &req);
mmcsd_host_unlock(host);
@@ -193,7 +241,7 @@ static rt_err_t rt_mmcsd_req_blk(struct rt_mmcsd_card *card,
{
LOG_E("mmcsd request blocks error");
LOG_E("%d,%d,%d, 0x%08x,0x%08x",
- cmd.err, data.err, stop.err, data.flags, sector);
+ cmd.err, data.err, stop.err, data.flags, sector);
return -RT_ERROR;
}
@@ -224,6 +272,8 @@ static rt_err_t rt_mmcsd_control(rt_device_t dev, int cmd, void *args)
case RT_DEVICE_CTRL_BLK_GETGEOME:
rt_memcpy(args, &blk_dev->geometry, sizeof(struct rt_device_blk_geometry));
break;
+ case RT_DEVICE_CTRL_BLK_PARTITION:
+ rt_memcpy(args, &blk_dev->part, sizeof(struct dfs_partition));
default:
break;
}
@@ -253,7 +303,7 @@ static rt_size_t rt_mmcsd_read(rt_device_t dev,
while (remain_size)
{
req_size = (remain_size > blk_dev->max_req_size) ? blk_dev->max_req_size : remain_size;
- err = rt_mmcsd_req_blk(blk_dev->card, part->offset + pos + offset, rd_ptr, req_size, 0);
+ err = rt_mmcsd_req_blk(blk_dev->card, pos + offset, rd_ptr, req_size, 0);
if (err)
break;
offset += req_size;
@@ -294,7 +344,7 @@ static rt_size_t rt_mmcsd_write(rt_device_t dev,
while (remain_size)
{
req_size = (remain_size > blk_dev->max_req_size) ? blk_dev->max_req_size : remain_size;
- err = rt_mmcsd_req_blk(blk_dev->card, part->offset + pos + offset, wr_ptr, req_size, 1);
+ err = rt_mmcsd_req_blk(blk_dev->card, pos + offset, wr_ptr, req_size, 1);
if (err)
break;
offset += req_size;
@@ -338,6 +388,19 @@ static rt_int32_t mmcsd_set_blksize(struct rt_mmcsd_card *card)
return 0;
}
+rt_int32_t read_lba(struct rt_mmcsd_card *card, size_t lba, uint8_t *buffer, size_t count)
+{
+ rt_uint8_t status = 0;
+
+ status = mmcsd_set_blksize(card);
+ if (status)
+ {
+ return status;
+ }
+ rt_thread_mdelay(1);
+ status = rt_mmcsd_req_blk(card, lba, buffer, count, 0);
+ return status;
+}
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops mmcsd_blk_ops =
@@ -351,44 +414,31 @@ const static struct rt_device_ops mmcsd_blk_ops =
};
#endif
-
-static struct mmcsd_blk_device * rt_mmcsd_create_blkdev(struct rt_mmcsd_card *card, const char* dname, struct dfs_partition* psPart)
+rt_int32_t gpt_device_probe(struct rt_mmcsd_card *card)
{
- struct mmcsd_blk_device *blk_dev;
- char sname[12];
+ rt_int32_t err = RT_EOK;
+ rt_uint8_t i, status;
+ char dname[10];
+ char sname[16];
+ struct mmcsd_blk_device *blk_dev = RT_NULL;
blk_dev = rt_calloc(1, sizeof(struct mmcsd_blk_device));
if (!blk_dev)
{
LOG_E("mmcsd:malloc memory failed!");
- return RT_NULL;
+ return -1;
}
- if (psPart != RT_NULL)
- {
- rt_memcpy(&blk_dev->part, psPart, sizeof(struct dfs_partition));
- blk_dev->geometry.sector_count = psPart->size;
- }
- else
- {
- blk_dev->part.offset = 0;
- blk_dev->part.size = 0;
- blk_dev->geometry.sector_count = card->card_capacity * (1024 / 512);
- }
-
- blk_dev->geometry.bytes_per_sector = 1<<9;
- blk_dev->geometry.block_size = card->card_blksize;
-
- rt_snprintf(sname, sizeof(sname), "sem_%s", dname);
- blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO);
-
blk_dev->max_req_size = BLK_MIN((card->host->max_dma_segs *
card->host->max_seg_size) >> 9,
(card->host->max_blk_count *
card->host->max_blk_size) >> 9);
-
+ blk_dev->part.offset = 0;
+ blk_dev->part.size = 0;
+ rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, 0);
+ blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO);
/* register mmcsd device */
- blk_dev->dev.type = RT_Device_Class_Block;
+ blk_dev->dev.type = RT_Device_Class_Block;
#ifdef RT_USING_DEVICE_OPS
blk_dev->dev.ops = &mmcsd_blk_ops;
#else
@@ -399,42 +449,100 @@ static struct mmcsd_blk_device * rt_mmcsd_create_blkdev(struct rt_mmcsd_card *ca
blk_dev->dev.write = rt_mmcsd_write;
blk_dev->dev.control = rt_mmcsd_control;
#endif
+ blk_dev->card = card;
+
+ blk_dev->geometry.bytes_per_sector = 1 << 9;
+ blk_dev->geometry.block_size = card->card_blksize;
+ blk_dev->geometry.sector_count =
+ card->card_capacity * (1024 / 512);
+
blk_dev->dev.user_data = blk_dev;
- blk_dev->card = card;
+ rt_device_register(&(blk_dev->dev), card->host->name,
+ RT_DEVICE_FLAG_RDWR);
+ rt_list_insert_after(&blk_devices, &blk_dev->list);
+
+ for (i = 0; i < RT_GPT_PARTITION_MAX; i++)
+ {
+ blk_dev = rt_calloc(1, sizeof(struct mmcsd_blk_device));
+ if (!blk_dev)
+ {
+ LOG_E("mmcsd:malloc memory failed!");
+ break;
+ }
+ blk_dev->max_req_size = BLK_MIN((card->host->max_dma_segs *
+ card->host->max_seg_size) >> 9,
+ (card->host->max_blk_count *
+ card->host->max_blk_size) >> 9);
- rt_device_register(&blk_dev->dev, dname,
- RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE | RT_DEVICE_FLAG_STANDALONE);
+ /* get the first partition */
+ status = gpt_get_partition_param(card, &blk_dev->part, i);
+ if (status == RT_EOK)
+ {
+ rt_snprintf(dname, sizeof(dname) - 1, "%s%d", card->host->name, i);
+ rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, i + 1);
+ blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO);
+
+ /* register mmcsd device */
+ blk_dev->dev.type = RT_Device_Class_Block;
+#ifdef RT_USING_DEVICE_OPS
+ blk_dev->dev.ops = &mmcsd_blk_ops;
+#else
+ blk_dev->dev.init = rt_mmcsd_init;
+ blk_dev->dev.open = rt_mmcsd_open;
+ blk_dev->dev.close = rt_mmcsd_close;
+ blk_dev->dev.read = rt_mmcsd_read;
+ blk_dev->dev.write = rt_mmcsd_write;
+ blk_dev->dev.control = rt_mmcsd_control;
+#endif
+ blk_dev->card = card;
- /* Insert to list. */
- rt_list_insert_after(&card->blk_devices, &blk_dev->list);
+ blk_dev->geometry.bytes_per_sector = 1 << 9;
+ blk_dev->geometry.block_size = card->card_blksize;
+ blk_dev->geometry.sector_count = blk_dev->part.size;
+
+ blk_dev->dev.user_data = blk_dev;
+
+ rt_device_register(&(blk_dev->dev), dname,
+ RT_DEVICE_FLAG_RDWR);
+ rt_list_insert_after(&blk_devices, &blk_dev->list);
+ }
+ else
+ {
+ rt_free(blk_dev);
+ blk_dev = RT_NULL;
+ break;
+ }
#ifdef RT_USING_DFS_MNTTABLE
- if ( blk_dev )
- {
- LOG_I("try to mount file system!");
- /* try to mount file system on this block device */
- dfs_mount_device(&(blk_dev->dev));
- }
+ if (blk_dev)
+ {
+ LOG_I("try to mount file system!");
+ /* try to mount file system on this block device */
+ dfs_mount_device(&(blk_dev->dev));
+ }
#endif
+ }
+ gpt_free();
- return blk_dev;
+ return err;
}
-rt_int32_t rt_mmcsd_blk_probe(struct rt_mmcsd_card *card)
+rt_int32_t mbr_device_probe(struct rt_mmcsd_card *card)
{
rt_int32_t err = 0;
- rt_err_t status;
+ rt_uint8_t i, status;
rt_uint8_t *sector;
+ char dname[10];
+ char sname[16];
+ struct mmcsd_blk_device *blk_dev = RT_NULL;
err = mmcsd_set_blksize(card);
- if(err)
+ if (err)
{
return err;
}
-
- LOG_D("probe mmcsd block device!");
-
+ rt_thread_mdelay(1);
/* get the first sector to read partition table */
sector = (rt_uint8_t *)rt_malloc(SECTOR_SIZE);
if (sector == RT_NULL)
@@ -447,43 +555,104 @@ rt_int32_t rt_mmcsd_blk_probe(struct rt_mmcsd_card *card)
status = rt_mmcsd_req_blk(card, 0, sector, 1, 0);
if (status == RT_EOK)
{
- rt_uint8_t i;
- char dname[8];
- struct dfs_partition part;
- struct mmcsd_blk_device *blk_dev = RT_NULL;
- rt_int32_t host_id = card->host->id;
+ blk_dev = rt_calloc(1, sizeof(struct mmcsd_blk_device));
+ if (!blk_dev)
+ {
+ LOG_E("mmcsd:malloc memory failed!");
+ return -1;
+ }
+ blk_dev->max_req_size = BLK_MIN((card->host->max_dma_segs *
+ card->host->max_seg_size) >> 9,
+ (card->host->max_blk_count *
+ card->host->max_blk_size) >> 9);
+ blk_dev->part.offset = 0;
+ blk_dev->part.size = 0;
+ rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, 0);
+ blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO);
+ /* register mmcsd device */
+ blk_dev->dev.type = RT_Device_Class_Block;
+#ifdef RT_USING_DEVICE_OPS
+ blk_dev->dev.ops = &mmcsd_blk_ops;
+#else
+ blk_dev->dev.init = rt_mmcsd_init;
+ blk_dev->dev.open = rt_mmcsd_open;
+ blk_dev->dev.close = rt_mmcsd_close;
+ blk_dev->dev.read = rt_mmcsd_read;
+ blk_dev->dev.write = rt_mmcsd_write;
+ blk_dev->dev.control = rt_mmcsd_control;
+#endif
+ blk_dev->card = card;
- /* Initial blk_device link-list. */
- rt_list_init(&card->blk_devices);
+ blk_dev->geometry.bytes_per_sector = 1 << 9;
+ blk_dev->geometry.block_size = card->card_blksize;
+ blk_dev->geometry.sector_count =
+ card->card_capacity * (1024 / 512);
+ blk_dev->dev.user_data = blk_dev;
+
+ rt_device_register(&(blk_dev->dev), card->host->name,
+ RT_DEVICE_FLAG_RDWR);
+ rt_list_insert_after(&blk_devices, &blk_dev->list);
for (i = 0; i < RT_MMCSD_MAX_PARTITION; i++)
{
- /* Get the first partition */
- status = dfs_filesystem_get_partition(&part, sector, i);
+ blk_dev = rt_calloc(1, sizeof(struct mmcsd_blk_device));
+ if (!blk_dev)
+ {
+ LOG_E("mmcsd:malloc memory failed!");
+ break;
+ }
+ blk_dev->max_req_size = BLK_MIN((card->host->max_dma_segs *
+ card->host->max_seg_size) >> 9,
+ (card->host->max_blk_count *
+ card->host->max_blk_size) >> 9);
+
+ /* get the first partition */
+ status = dfs_filesystem_get_partition(&blk_dev->part, sector, i);
if (status == RT_EOK)
{
- /* Given name is with allocated host id and its partition index. */
- rt_snprintf(dname, sizeof(dname), "sd%dp%d", host_id, i);
- blk_dev = rt_mmcsd_create_blkdev(card, (const char*)dname, &part);
- if ( blk_dev == RT_NULL )
- {
- err = -RT_ENOMEM;
- goto exit_rt_mmcsd_blk_probe;
- }
+ rt_snprintf(dname, sizeof(dname) - 1, "%s%d", card->host->name, i);
+ rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, i + 1);
+ blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO);
+
+ /* register mmcsd device */
+ blk_dev->dev.type = RT_Device_Class_Block;
+#ifdef RT_USING_DEVICE_OPS
+ blk_dev->dev.ops = &mmcsd_blk_ops;
+#else
+ blk_dev->dev.init = rt_mmcsd_init;
+ blk_dev->dev.open = rt_mmcsd_open;
+ blk_dev->dev.close = rt_mmcsd_close;
+ blk_dev->dev.read = rt_mmcsd_read;
+ blk_dev->dev.write = rt_mmcsd_write;
+ blk_dev->dev.control = rt_mmcsd_control;
+#endif
+ blk_dev->card = card;
+
+ blk_dev->geometry.bytes_per_sector = 1 << 9;
+ blk_dev->geometry.block_size = card->card_blksize;
+ blk_dev->geometry.sector_count = blk_dev->part.size;
+
+ blk_dev->dev.user_data = blk_dev;
+
+ rt_device_register(&(blk_dev->dev), dname,
+ RT_DEVICE_FLAG_RDWR);
+ rt_list_insert_after(&blk_devices, &blk_dev->list);
}
else
{
+ rt_free(blk_dev);
+ blk_dev = RT_NULL;
break;
}
- }
- /* Always create the super node, given name is with allocated host id. */
- rt_snprintf(dname, sizeof(dname), "sd%d", host_id);
- blk_dev = rt_mmcsd_create_blkdev(card, (const char*)dname, RT_NULL);
- if ( blk_dev == RT_NULL )
- {
- err = -RT_ENOMEM;
- goto exit_rt_mmcsd_blk_probe;
+#ifdef RT_USING_DFS_MNTTABLE
+ if (blk_dev)
+ {
+ LOG_I("try to mount file system!");
+ /* try to mount file system on this block device */
+ dfs_mount_device(&(blk_dev->dev));
+ }
+#endif
}
}
else
@@ -492,42 +661,45 @@ rt_int32_t rt_mmcsd_blk_probe(struct rt_mmcsd_card *card)
err = -RT_ERROR;
}
-exit_rt_mmcsd_blk_probe:
-
/* release sector buffer */
rt_free(sector);
return err;
+
}
-void rt_mmcsd_blk_remove(struct rt_mmcsd_card *card)
+rt_int32_t rt_mmcsd_blk_probe(struct rt_mmcsd_card *card)
{
- rt_list_t *l, *n;
- struct mmcsd_blk_device *blk_dev;
+ uint32_t err = 0;
- if(card == RT_NULL)
+ LOG_D("probe mmcsd block device!");
+ if (check_gpt(card) != 0)
{
- LOG_E("card is null!");
- return;
+ err = gpt_device_probe(card);
}
-
- if(rt_list_isempty(&card->blk_devices))
+ else
{
- LOG_E("card blk_devices is empty!");
- return;
+ err = mbr_device_probe(card);
}
+ return err;
+}
- for (l = (&card->blk_devices)->next, n = l->next; l != &card->blk_devices; l = n, n=n->next)
+void rt_mmcsd_blk_remove(struct rt_mmcsd_card *card)
+{
+ rt_list_t *l, *n;
+ struct mmcsd_blk_device *blk_dev;
+
+ for (l = (&blk_devices)->next, n = l->next; l != &blk_devices; l = n, n = n->next)
{
blk_dev = (struct mmcsd_blk_device *)rt_list_entry(l, struct mmcsd_blk_device, list);
if (blk_dev->card == card)
{
/* unmount file system */
- const char * mounted_path = dfs_filesystem_get_mounted_path(&(blk_dev->dev));
+ const char *mounted_path = dfs_filesystem_get_mounted_path(&(blk_dev->dev));
if (mounted_path)
{
- dfs_unmount(mounted_path);
- LOG_D("unmount file system %s for device %s.\r\n", mounted_path, blk_dev->dev.parent.name);
+ dfs_unmount(mounted_path);
+ LOG_D("unmount file system %s for device %s.\r\n", mounted_path, blk_dev->dev.parent.name);
}
rt_sem_delete(blk_dev->part.lock);
rt_device_unregister(&blk_dev->dev);
@@ -536,3 +708,15 @@ void rt_mmcsd_blk_remove(struct rt_mmcsd_card *card)
}
}
}
+
+/*
+ * This function will initialize block device on the mmc/sd.
+ *
+ * @deprecated since 2.1.0, this function does not need to be invoked
+ * in the system initialization.
+ */
+int rt_mmcsd_blk_init(void)
+{
+ /* nothing */
+ return 0;
+}
diff --git a/components/drivers/sdio/gpt.c b/components/drivers/sdio/gpt.c
new file mode 100644
index 0000000000..19bc1ad453
--- /dev/null
+++ b/components/drivers/sdio/gpt.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-05-05 linzhenxing first version
+ */
+#include <rtthread.h>
+#include <dfs_fs.h>
+#include <drivers/gpt.h>
+#include <drivers/mmcsd_core.h>
+
+#define DBG_TAG "GPT"
+#ifdef RT_SDIO_DEBUG
+#define DBG_LVL DBG_LOG
+#else
+#define DBG_LVL DBG_INFO
+#endif /* RT_SDIO_DEBUG */
+#include <rtdbg.h>
+
+#define min(a, b) a < b ? a : b
+static int force_gpt = 0;
+static gpt_header *_gpt;
+static gpt_entry *_ptes;
+#define GPT_TYPE 1
+#define MBR_TYPE 0
+
+static inline int efi_guidcmp (gpt_guid_t left, gpt_guid_t right)
+{
+ return rt_memcmp(&left, &right, sizeof (gpt_guid_t));
+}
+
+static uint32_t last_lba(struct rt_mmcsd_card *card)
+{
+ RT_ASSERT(card != RT_NULL);
+ return (card->card_sec_cnt) - 1;
+}
+
+static inline int pmbr_part_valid(gpt_mbr_record *part)
+{
+ if (part->os_type != EFI_PMBR_OSTYPE_EFI_GPT)
+ {
+ goto invalid;
+ }
+
+ /* set to 0x00000001 (i.e., the LBA of the GPT Partition Header) */
+ if ((uint32_t)(part->starting_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA)
+ {
+ goto invalid;
+ }
+
+ return GPT_MBR_PROTECTIVE;
+invalid:
+ return 0;
+}
+
+/*
+*
+* return ret
+* ret = 0, invalid mbr
+* ret = 1, protect mbr
+* ret = 2, hybrid mbr
+*/
+int is_pmbr_valid(legacy_mbr *mbr, uint64_t total_sectors)
+{
+ uint32_t sz = 0;
+ int i, part = 0, ret = 0; /* invalid by default */
+
+ if (!mbr || (uint16_t)(mbr->signature) != MSDOS_MBR_SIGNATURE)
+ {
+ goto done;
+ }
+
+ for (i = 0; i < 4; i++)
+ {
+ ret = pmbr_part_valid(&mbr->partition_record[i]);
+ if (ret == GPT_MBR_PROTECTIVE)
+ {
+ part = i;
+ /*
+ * Ok, we at least know that there's a protective MBR,
+ * now check if there are other partition types for
+ * hybrid MBR.
+ */
+ goto check_hybrid;
+ }
+ }
+
+ if (ret != GPT_MBR_PROTECTIVE)
+ {
+ goto done;
+ }
+
+check_hybrid:
+ for (i = 0; i < 4; i++)
+ {
+ if ((mbr->partition_record[i].os_type !=
+ EFI_PMBR_OSTYPE_EFI_GPT) &&
+ (mbr->partition_record[i].os_type != 0x00))
+ {
+ ret = GPT_MBR_HYBRID;
+ }
+
+ }
+
+ /*
+ * Protective MBRs take up the lesser of the whole disk
+ * or 2 TiB (32bit LBA), ignoring the rest of the disk.
+ * Some partitioning programs, nonetheless, choose to set
+ * the size to the maximum 32-bit limitation, disregarding
+ * the disk size.
+ *
+ * Hybrid MBRs do not necessarily comply with this.
+ *
+ * Consider a bad value here to be a warning to support dd'ing
+ * an image from a smaller disk to a larger disk.
+ */
+ if (ret == GPT_MBR_PROTECTIVE)
+ {
+ sz = (uint32_t)(mbr->partition_record[part].size_in_lba);
+ if (sz != (uint32_t) total_sectors - 1 && sz != 0xFFFFFFFF)
+ {
+ LOG_I("GPT: mbr size in lba (%u) different than whole disk (%u).",
+ sz, min(total_sectors - 1, 0xFFFFFFFF));
+ }
+ }
+
+done:
+ return ret;
+
+}
+
+static gpt_entry *alloc_read_gpt_entries(struct rt_mmcsd_card *card, gpt_header *gpt)
+{
+ size_t count;
+ gpt_entry *pte;
+
+ if (!gpt)
+ {
+ return RT_NULL;
+ }
+
+ count = (size_t)(gpt->num_partition_entries) * (gpt->sizeof_partition_entry);
+ if (!count)
+ {
+ return RT_NULL;
+ }
+
+ pte = rt_malloc(count);
+ if (!pte)
+ return RT_NULL;
+
+ if (read_lba(card, (size_t)(gpt->partition_entry_lba),(uint8_t *)pte, count/512) != RT_EOK)
+ {
+ rt_free(pte);
+ return RT_NULL;
+ }
+ return pte;
+
+}
+
+static gpt_header *alloc_read_gpt_header(struct rt_mmcsd_card *card, size_t lba)
+{
+ gpt_header *gpt;
+ void *buf;
+
+ buf = rt_malloc(512);
+ if (!buf)
+ {
+ return RT_NULL;
+ }
+
+ if (read_lba(card, lba, (uint8_t *)buf, 1) != RT_EOK)
+ {
+ rt_free(buf);
+ return RT_NULL;
+ }
+ gpt = (gpt_header *)buf;
+
+ return gpt;
+}
+
+static int is_gpt_valid(struct rt_mmcsd_card *card, size_t lba, gpt_header **gpt, gpt_entry **ptes)
+{
+ size_t lastlba;
+
+ if (!ptes)
+ {
+ return 0;
+ }
+
+ if (!(*gpt = alloc_read_gpt_header(card, lba)))
+ {
+ return 0;
+ }
+
+ /* Check the GUID Partition Table signature */
+ if ((uint64_t)((*gpt)->signature) != GPT_HEADER_SIGNATURE)
+ {
+ LOG_E("GUID Partition Table Header signature is wrong:"
+ "%ld != %ld",(uint64_t)((*gpt)->signature),(uint64_t)GPT_HEADER_SIGNATURE);
+ goto fail;
+ }
+
+ /* Check the GUID Partition Table header size is too small */
+ if ((uint32_t)((*gpt)->header_size) < sizeof(gpt_header))
+ {
+ LOG_E("GUID Partition Table Header size is too small: %u < %zu",
+ (uint32_t)((*gpt)->header_size),sizeof(gpt_header));
+ goto fail;
+ }
+
+ /* Check that the start_lba entry points to the LBA that contains
+ * the GUID Partition Table */
+ if ((uint64_t)((*gpt)->start_lba) != lba)
+ {
+ LOG_E("GPT start_lba incorrect: %ld != %ld",
+ (uint64_t)((*gpt)->start_lba),
+ (uint64_t)lba);
+ goto fail;
+ }
+
+ /* Check the first_usable_lba and last_usable_lba are
+ * within the disk.
+ */
+ lastlba = last_lba(card);
+ if ((uint64_t)((*gpt)->first_usable_lba) > lastlba)
+ {
+ LOG_E("GPT: first_usable_lba incorrect: %ld > %ld",
+ ((uint64_t)((*gpt)->first_usable_lba)),
+ (size_t)lastlba);
+ goto fail;
+ }
+
+ if ((uint64_t)((*gpt)->last_usable_lba) > lastlba)
+ {
+ LOG_E("GPT: last_usable_lba incorrect: %ld > %ld",
+ (uint64_t)((*gpt)->last_usable_lba),
+ (size_t)lastlba);
+ goto fail;
+ }
+
+ if ((uint64_t)((*gpt)->last_usable_lba) < (uint64_t)((*gpt)->first_usable_lba))
+ {
+ LOG_E("GPT: last_usable_lba incorrect: %ld > %ld",
+ (uint64_t)((*gpt)->last_usable_lba),
+ (uint64_t)((*gpt)->first_usable_lba));
+ goto fail;
+ }
+ /* Check that sizeof_partition_entry has the correct value */
+ if ((uint32_t)((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) {
+ LOG_E("GUID Partition Entry Size check failed.");
+ goto fail;
+ }
+
+ if (!(*ptes = alloc_read_gpt_entries(card, *gpt)))
+ {
+ goto fail;
+ }
+
+ /* We're done, all's well */
+ return 1;
+
+ fail:
+ rt_free(*gpt);
+ *gpt = RT_NULL;
+ return 0;
+}
+
+/**
+ * is_pte_valid() - tests one PTE for validity
+ * pte:pte to check
+ * lastlba: last lba of the disk
+ *
+ * Description: returns 1 if valid, 0 on error.
+ */
+static inline int is_pte_valid(const gpt_entry *pte, const size_t lastlba)
+{
+ if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) ||
+ (uint64_t)(pte->starting_lba) > lastlba ||
+ (uint64_t)(pte->ending_lba) > lastlba)
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * compare_gpts() - Search disk for valid GPT headers and PTEs
+ * pgpt: primary GPT header
+ * agpt: alternate GPT header
+ * lastlba: last LBA number
+ *
+ * Description: Returns nothing. Sanity checks pgpt and agpt fields
+ * and prints warnings on discrepancies.
+ *
+ */
+static void compare_gpts(gpt_header *pgpt, gpt_header *agpt, size_t lastlba)
+{
+ int error_found = 0;
+ if (!pgpt || !agpt)
+ {
+ return;
+ }
+
+ if ((uint64_t)(pgpt->start_lba) != (uint64_t)(agpt->alternate_lba))
+ {
+ LOG_I("GPT:Primary header LBA != Alt. header alternate_lba");
+ LOG_I("GPT:%lld != %lld",
+ (uint64_t)(pgpt->start_lba),
+ (uint64_t)(agpt->alternate_lba));
+ error_found++;
+ }
+
+ if ((uint64_t)(pgpt->alternate_lba) != (uint64_t)(agpt->start_lba))
+ {
+ LOG_I("GPT:Primary header alternate_lba != Alt. header start_lba");
+ LOG_I("GPT:%lld != %lld",
+ (uint64_t)(pgpt->alternate_lba),
+ (uint64_t)(agpt->start_lba));
+ error_found++;
+ }
+
+ if ((uint64_t)(pgpt->first_usable_lba) != (uint64_t)(agpt->first_usable_lba))
+ {
+ LOG_I("GPT:first_usable_lbas don't match.");
+ LOG_I("GPT:%lld != %lld",
+ (uint64_t)(pgpt->first_usable_lba),
+ (uint64_t)(agpt->first_usable_lba));
+ error_found++;
+ }
+
+ if ((uint64_t)(pgpt->last_usable_lba) != (uint64_t)(agpt->last_usable_lba))
+ {
+ LOG_I("GPT:last_usable_lbas don't match.");
+ LOG_I("GPT:%lld != %lld",
+ (uint64_t)(pgpt->last_usable_lba),
+ (uint64_t)(agpt->last_usable_lba));
+ error_found++;
+ }
+
+ if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid))
+ {
+ LOG_I("GPT:disk_guids don't match.");
+ error_found++;
+ }
+
+ if ((pgpt->num_partition_entries) != (agpt->num_partition_entries))
+ {
+ LOG_I("GPT:num_partition_entries don't match: "
+ "0x%x != 0x%x",
+ (pgpt->num_partition_entries),
+ (agpt->num_partition_entries));
+ error_found++;
+ }
+
+ if ((pgpt->sizeof_partition_entry) != (agpt->sizeof_partition_entry))
+ {
+ LOG_I("GPT:sizeof_partition_entry values don't match: "
+ "0x%x != 0x%x",
+ (pgpt->sizeof_partition_entry),
+ (agpt->sizeof_partition_entry));
+ error_found++;
+ }
+
+ if ((pgpt->partition_entry_array_crc32) != (agpt->partition_entry_array_crc32))
+ {
+ LOG_I("GPT:partition_entry_array_crc32 values don't match: "
+ "0x%x != 0x%x",
+ (pgpt->partition_entry_array_crc32),
+ (agpt->partition_entry_array_crc32));
+ error_found++;
+ }
+
+ if ((pgpt->alternate_lba) != lastlba)
+ {
+ LOG_I("GPT:Primary header thinks Alt. header is not at the end of the disk.");
+ LOG_I("GPT:%lld != %lld",
+ (uint64_t)(pgpt->alternate_lba),
+ (size_t)lastlba);
+ error_found++;
+ }
+
+ if ((agpt->start_lba) != lastlba)
+ {
+ LOG_I("GPT:Alternate GPT header not at the end of the disk.");
+ LOG_I("GPT:%lld != %lld",
+ (uint64_t)(agpt->start_lba),
+ (size_t)lastlba);
+ error_found++;
+ }
+
+ if (error_found)
+ {
+ LOG_I("GPT: Use GNU Parted to correct GPT errors.");
+ }
+ return;
+}
+
+/**
+ * find_valid_gpt() - Search disk for valid GPT headers and PTEs
+ * state: disk parsed partitions
+ * gpt: GPT header ptr, filled on return.
+ * ptes: PTEs ptr, filled on return.
+ *
+ * Description: Returns 1 if valid, 0 on error.
+ * If valid, returns pointers to newly allocated GPT header and PTEs.
+ * Validity depends on PMBR being valid (or being overridden by the
+ * 'gpt' kernel command line option) and finding either the Primary
+ * GPT header and PTEs valid, or the Alternate GPT header and PTEs
+ * valid. If the Primary GPT header is not valid, the Alternate GPT header
+ * is not checked unless the 'gpt' kernel command line option is passed.
+ * This protects against devices which misreport their size, and forces
+ * the user to decide to use the Alternate GPT.
+ */
+static int find_valid_gpt(struct rt_mmcsd_card *card, gpt_header **gpt,
+ gpt_entry **ptes)
+{
+ int good_pgpt = 0, good_agpt = 0, good_pmbr = 0;
+ gpt_header *pgpt = RT_NULL, *agpt = RT_NULL;
+ gpt_entry *pptes = RT_NULL, *aptes = RT_NULL;
+ legacy_mbr *legacymbr;
+ size_t total_sectors = last_lba(card) + 1;
+ size_t lastlba;
+ int status = 0;
+
+ if (!ptes)
+ {
+ return 0;
+ }
+
+ lastlba = last_lba(card);
+ if (!force_gpt)
+ {
+ /* This will be added to the EFI Spec. per Intel after v1.02. */
+ legacymbr = rt_malloc(512);
+ if (!legacymbr)
+ {
+ goto fail;
+ }
+
+ status = read_lba(card, 0, (uint8_t *)legacymbr, 1);
+ if (status)
+ {
+ LOG_I("status:%d", status);
+ goto fail;
+ }
+
+ good_pmbr = is_pmbr_valid(legacymbr, total_sectors);
+ rt_free(legacymbr);
+
+ if (!good_pmbr)
+ {
+ goto fail;
+ }
+
+ rt_kprintf("Device has a %s MBR\n",
+ good_pmbr == GPT_MBR_PROTECTIVE ?
+ "protective" : "hybrid");
+ }
+
+ good_pgpt = is_gpt_valid(card, GPT_PRIMARY_PARTITION_TABLE_LBA,
+ &pgpt, &pptes);
+ if (good_pgpt)
+ {
+ good_agpt = is_gpt_valid(card, (pgpt->alternate_lba), &agpt, &aptes);
+ if (!good_agpt && force_gpt)
+ {
+ good_agpt = is_gpt_valid(card, lastlba, &agpt, &aptes);
+ }
+
+ /* The obviously unsuccessful case */
+ if (!good_pgpt && !good_agpt)
+ {
+ goto fail;
+ }
+
+ compare_gpts(pgpt, agpt, lastlba);
+
+ /* The good cases */
+ if (good_pgpt)
+ {
+ *gpt = pgpt;
+ *ptes = pptes;
+ rt_free(agpt);
+ rt_free(aptes);
+ if (!good_agpt)
+ {
+ LOG_D("Alternate GPT is invalid, using primary GPT.");
+ }
+ return 1;
+ }
+ else if (good_agpt)
+ {
+ *gpt = agpt;
+ *ptes = aptes;
+ rt_free(pgpt);
+ rt_free(pptes);
+ LOG_D("Primary GPT is invalid, using alternate GPT.");
+ return 1;
+ }
+ }
+
+ fail:
+ rt_free(pgpt);
+ rt_free(agpt);
+ rt_free(pptes);
+ rt_free(aptes);
+ *gpt = RT_NULL;
+ *ptes = RT_NULL;
+ return 0;
+}
+
+int check_gpt(struct rt_mmcsd_card *card)
+{
+ if (!find_valid_gpt(card, &_gpt, &_ptes) || !_gpt || !_ptes)
+ {
+ rt_free(_gpt);
+ rt_free(_ptes);
+ return MBR_TYPE;
+ }
+ return GPT_TYPE;
+}
+
+int gpt_get_partition_param(struct rt_mmcsd_card *card, struct dfs_partition *part, uint32_t pindex)
+{
+ if (!is_pte_valid(&_ptes[pindex], last_lba(card)))
+ {
+ return -1;
+ }
+
+ part->offset = (off_t)(_ptes[pindex].starting_lba);
+ part->size = (_ptes[pindex].ending_lba) - (_ptes[pindex].starting_lba) + 1ULL;
+
+ rt_kprintf("found part[%d], begin(sector): %d, end(sector):%d size: ",
+ pindex, _ptes[pindex].starting_lba, _ptes[pindex].ending_lba);
+
+ if ((part->size >> 11) == 0)
+ {
+ rt_kprintf("%d%s", part->size >> 1, "KB\n"); /* KB */
+ }
+ else
+ {
+ unsigned int part_size;
+ part_size = part->size >> 11; /* MB */
+ if ((part_size >> 10) == 0)
+ rt_kprintf("%d.%d%s", part_size, (part->size >> 1) & 0x3FF, "MB\n");
+ else
+ rt_kprintf("%d.%d%s", part_size >> 10, part_size & 0x3FF, "GB\n");
+ }
+ return 0;
+}
+
+void gpt_free(void)
+{
+ rt_free(_ptes);
+ rt_free(_gpt);
+}
diff --git a/components/drivers/sdio/mmc.c b/components/drivers/sdio/mmc.c
index 0365de8ed7..32a9a204d8 100644
--- a/components/drivers/sdio/mmc.c
+++ b/components/drivers/sdio/mmc.c
@@ -46,17 +46,17 @@ rt_inline rt_uint32_t GET_BITS(rt_uint32_t *resp,
rt_uint32_t start,
rt_uint32_t size)
{
- const rt_int32_t __size = size;
- const rt_uint32_t __mask = (__size < 32 ? 1 << __size : 0) - 1;
- const rt_int32_t __off = 3 - ((start) / 32);
- const rt_int32_t __shft = (start) & 31;
- rt_uint32_t __res;
+ const rt_int32_t __size = size;
+ const rt_uint32_t __mask = (__size < 32 ? 1 << __size : 0) - 1;
+ const rt_int32_t __off = 3 - ((start) / 32);
+ const rt_int32_t __shft = (start) & 31;
+ rt_uint32_t __res;
- __res = resp[__off] >> __shft;
- if (__size + __shft > 32)
- __res |= resp[__off-1] << ((32 - __shft) % 32);
+ __res = resp[__off] >> __shft;
+ if (__size + __shft > 32)
+ __res |= resp[__off - 1] << ((32 - __shft) % 32);
- return __res & __mask;
+ return __res & __mask;
}
/*
@@ -64,50 +64,52 @@ rt_inline rt_uint32_t GET_BITS(rt_uint32_t *resp,
*/
static rt_int32_t mmcsd_parse_csd(struct rt_mmcsd_card *card)
{
- rt_uint32_t a, b;
- struct rt_mmcsd_csd *csd = &card->csd;
- rt_uint32_t *resp = card->resp_csd;
+ rt_uint32_t a, b;
+ struct rt_mmcsd_csd *csd = &card->csd;
+ rt_uint32_t *resp = card->resp_csd;
- /*
- * We only understand CSD structure v1.1 and v1.2.
- * v1.2 has extra information in bits 15, 11 and 10.
- * We also support eMMC v4.4 & v4.41.
- */
- csd->csd_structure = GET_BITS(resp, 126, 2);
- if (csd->csd_structure == 0) {
+ /*
+ * We only understand CSD structure v1.1 and v1.2.
+ * v1.2 has extra information in bits 15, 11 and 10.
+ * We also support eMMC v4.4 & v4.41.
+ */
+ csd->csd_structure = GET_BITS(resp, 126, 2);
+ if (csd->csd_structure == 0)
+ {
LOG_E("unrecognised CSD structure version %d!", csd->csd_structure);
return -RT_ERROR;
- }
+ }
- csd->taac = GET_BITS(resp, 112, 8);
- csd->nsac = GET_BITS(resp, 104, 8);
- csd->tran_speed = GET_BITS(resp, 96, 8);
- csd->card_cmd_class = GET_BITS(resp, 84, 12);
- csd->rd_blk_len = GET_BITS(resp, 80, 4);
- csd->rd_blk_part = GET_BITS(resp, 79, 1);
- csd->wr_blk_misalign = GET_BITS(resp, 78, 1);
- csd->rd_blk_misalign = GET_BITS(resp, 77, 1);
- csd->dsr_imp = GET_BITS(resp, 76, 1);
- csd->c_size = GET_BITS(resp, 62, 12);
- csd->c_size_mult = GET_BITS(resp, 47, 3);
- csd->r2w_factor = GET_BITS(resp, 26, 3);
- csd->wr_blk_len = GET_BITS(resp, 22, 4);
- csd->wr_blk_partial = GET_BITS(resp, 21, 1);
- csd->csd_crc = GET_BITS(resp, 1, 7);
+ csd->taac = GET_BITS(resp, 112, 8);
+ csd->nsac = GET_BITS(resp, 104, 8);
+ csd->tran_speed = GET_BITS(resp, 96, 8);
+ csd->card_cmd_class = GET_BITS(resp, 84, 12);
+ csd->rd_blk_len = GET_BITS(resp, 80, 4);
+ csd->rd_blk_part = GET_BITS(resp, 79, 1);
+ csd->wr_blk_misalign = GET_BITS(resp, 78, 1);
+ csd->rd_blk_misalign = GET_BITS(resp, 77, 1);
+ csd->dsr_imp = GET_BITS(resp, 76, 1);
+ csd->c_size = GET_BITS(resp, 62, 12);
+ csd->c_size_mult = GET_BITS(resp, 47, 3);
+ csd->r2w_factor = GET_BITS(resp, 26, 3);
+ csd->wr_blk_len = GET_BITS(resp, 22, 4);
+ csd->wr_blk_partial = GET_BITS(resp, 21, 1);
+ csd->csd_crc = GET_BITS(resp, 1, 7);
- card->card_blksize = 1 << csd->rd_blk_len;
- card->tacc_clks = csd->nsac * 100;
- card->tacc_ns = (tacc_uint[csd->taac&0x07] * tacc_value[(csd->taac&0x78)>>3] + 9) / 10;
- card->max_data_rate = tran_unit[csd->tran_speed&0x07] * tran_value[(csd->tran_speed&0x78)>>3];
- if (csd->wr_blk_len >= 9) {
+ card->card_blksize = 1 << csd->rd_blk_len;
+ card->tacc_clks = csd->nsac * 100;
+ card->tacc_ns = (tacc_uint[csd->taac & 0x07] * tacc_value[(csd->taac & 0x78) >> 3] + 9) / 10;
+ card->max_data_rate = tran_unit[csd->tran_speed & 0x07] * tran_value[(csd->tran_speed & 0x78) >> 3];
+ if (csd->wr_blk_len >= 9)
+ {
a = GET_BITS(resp, 42, 5);
b = GET_BITS(resp, 37, 5);
card->erase_size = (a + 1) * (b + 1);
card->erase_size <<= csd->wr_blk_len - 9;
- }
+ }
- return 0;
+ return 0;
}
/*
@@ -115,66 +117,66 @@ static rt_int32_t mmcsd_parse_csd(struct rt_mmcsd_card *card)
*/
static int mmc_get_ext_csd(struct rt_mmcsd_card *card, rt_uint8_t **new_ext_csd)
{
- void *ext_csd;
- struct rt_mmcsd_req req;
- struct rt_mmcsd_cmd cmd;
- struct rt_mmcsd_data data;
-
- *new_ext_csd = RT_NULL;
+ void *ext_csd;
+ struct rt_mmcsd_req req;
+ struct rt_mmcsd_cmd cmd;
+ struct rt_mmcsd_data data;
- if (GET_BITS(card->resp_csd, 122, 4) < 4)
- return 0;
+ *new_ext_csd = RT_NULL;
+ if (GET_BITS(card->resp_csd, 122, 4) < 4)
+ return 0;
- /*
- * As the ext_csd is so large and mostly unused, we don't store the
- * raw block in mmc_card.
- */
- ext_csd = rt_malloc(512);
- if (!ext_csd) {
- LOG_E("alloc memory failed when get ext csd!");
- return -RT_ENOMEM;
- }
+ /*
+ * As the ext_csd is so large and mostly unused, we don't store the
+ * raw block in mmc_card.
+ */
+ ext_csd = rt_malloc(512);
+ if (!ext_csd)
+ {
+ LOG_E("alloc memory failed when get ext csd!");
+ return -RT_ENOMEM;
+ }
- rt_memset(&req, 0, sizeof(struct rt_mmcsd_req));
- rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd));
- rt_memset(&data, 0, sizeof(struct rt_mmcsd_data));
+ rt_memset(&req, 0, sizeof(struct rt_mmcsd_req));
+ rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd));
+ rt_memset(&data, 0, sizeof(struct rt_mmcsd_data));
- req.cmd = &cmd;
- req.data = &data;
+ req.cmd = &cmd;
+ req.data = &data;
- cmd.cmd_code = SEND_EXT_CSD;
- cmd.arg = 0;
+ cmd.cmd_code = SEND_EXT_CSD;
+ cmd.arg = 0;
- /* NOTE HACK: the RESP_SPI_R1 is always correct here, but we
- * rely on callers to never use this with "native" calls for reading
- * CSD or CID. Native versions of those commands use the R2 type,
- * not R1 plus a data block.
- */
- cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC;
+ /* NOTE HACK: the RESP_SPI_R1 is always correct here, but we
+ * rely on callers to never use this with "native" calls for reading
+ * CSD or CID. Native versions of those commands use the R2 type,
+ * not R1 plus a data block.
+ */
+ cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC;
- data.blksize = 512;
- data.blks = 1;
- data.flags = DATA_DIR_READ;
- data.buf = ext_csd;
+ data.blksize = 512;
+ data.blks = 1;
+ data.flags = DATA_DIR_READ;
+ data.buf = ext_csd;
- /*
- * Some cards require longer data read timeout than indicated in CSD.
- * Address this by setting the read timeout to a "reasonably high"
- * value. For the cards tested, 300ms has proven enough. If necessary,
- * this value can be increased if other problematic cards require this.
- */
- data.timeout_ns = 300000000;
- data.timeout_clks = 0;
+ /*
+ * Some cards require longer data read timeout than indicated in CSD.
+ * Address this by setting the read timeout to a "reasonably high"
+ * value. For the cards tested, 300ms has proven enough. If necessary,
+ * this value can be increased if other problematic cards require this.
+ */
+ data.timeout_ns = 300000000;
+ data.timeout_clks = 0;
- mmcsd_send_request(card->host, &req);
+ mmcsd_send_request(card->host, &req);
- if (cmd.err)
- return cmd.err;
- if (data.err)
- return data.err;
+ if (cmd.err)
+ return cmd.err;
+ if (data.err)
+ return data.err;
- *new_ext_csd = ext_csd;
- return 0;
+ *new_ext_csd = ext_csd;
+ return 0;
}
/*
@@ -182,24 +184,39 @@ static int mmc_get_ext_csd(struct rt_mmcsd_card *card, rt_uint8_t **new_ext_csd)
*/
static int mmc_parse_ext_csd(struct rt_mmcsd_card *card, rt_uint8_t *ext_csd)
{
- rt_uint64_t card_capacity = 0;
-
- if(card == RT_NULL || ext_csd == RT_NULL)
- {
- LOG_E("emmc parse ext csd fail, invaild args");
- return -1;
- }
+ rt_uint64_t card_capacity = 0;
+ struct rt_mmcsd_host *host;
+ if (card == RT_NULL || ext_csd == RT_NULL)
+ {
+ LOG_E("emmc parse ext csd fail, invaild args");
+ return -1;
+ }
- card->flags |= CARD_FLAG_HIGHSPEED;
- card->hs_max_data_rate = 52000000;
+ host = card->host;
+ if (host->flags & MMCSD_SUP_HS200)
+ {
+ card->flags |= CARD_FLAG_HS200;
+ card->hs_max_data_rate = 200000000;
+ }
+ else if (host->flags & MMCSD_SUP_HIGHSPEED_DDR)
+ {
+ card->flags |= CARD_FLAG_HIGHSPEED_DDR;
+ card->hs_max_data_rate = 52000000;
+ }
+ else
+ {
+ card->flags |= CARD_FLAG_HIGHSPEED;
+ card->hs_max_data_rate = 52000000;
+ }
- card_capacity = *((rt_uint32_t *)&ext_csd[EXT_CSD_SEC_CNT]);
- card_capacity *= card->card_blksize;
- card_capacity >>= 10; /* unit:KB */
- card->card_capacity = (rt_uint32_t)card_capacity;
- LOG_I("emmc card capacity %d KB.", card->card_capacity);
+ card_capacity = *((rt_uint32_t *)&ext_csd[EXT_CSD_SEC_CNT]);
+ card->card_sec_cnt = card_capacity;
+ card_capacity *= card->card_blksize;
+ card_capacity >>= 10; /* unit:KB */
+ card->card_capacity = card_capacity;
+ LOG_I("emmc card capacity %d KB, card sec count:%d.", card->card_capacity, card->card_sec_cnt);
- return 0;
+ return 0;
}
/**
@@ -214,72 +231,73 @@ static int mmc_parse_ext_csd(struct rt_mmcsd_card *card, rt_uint8_t *ext_csd)
static int mmc_switch(struct rt_mmcsd_card *card, rt_uint8_t set,
rt_uint8_t index, rt_uint8_t value)
{
- int err;
- struct rt_mmcsd_host *host = card->host;
- struct rt_mmcsd_cmd cmd = {0};
+ int err;
+ struct rt_mmcsd_host *host = card->host;
+ struct rt_mmcsd_cmd cmd = {0};
- cmd.cmd_code = SWITCH;
- cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
- (index << 16) | (value << 8) | set;
- cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_AC;
+ cmd.cmd_code = SWITCH;
+ cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
+ (index << 16) | (value << 8) | set;
+ cmd.flags = RESP_R1B | CMD_AC;
- err = mmcsd_send_cmd(host, &cmd, 3);
- if (err)
- return err;
+ err = mmcsd_send_cmd(host, &cmd, 3);
+ if (err)
+ return err;
- return 0;
+ return 0;
}
static int mmc_compare_ext_csds(struct rt_mmcsd_card *card,
rt_uint8_t *ext_csd, rt_uint32_t bus_width)
{
- rt_uint8_t *bw_ext_csd;
- int err;
+ rt_uint8_t *bw_ext_csd;
+ int err;
- if (bus_width == MMCSD_BUS_WIDTH_1)
- return 0;
+ if (bus_width == MMCSD_BUS_WIDTH_1)
+ return 0;
- err = mmc_get_ext_csd(card, &bw_ext_csd);
+ err = mmc_get_ext_csd(card, &bw_ext_csd);
- if (err || bw_ext_csd == RT_NULL) {
- err = -RT_ERROR;
- goto out;
- }
+ if (err || bw_ext_csd == RT_NULL)
+ {
+ err = -RT_ERROR;
+ goto out;
+ }
- /* only compare read only fields */
- err = !((ext_csd[EXT_CSD_PARTITION_SUPPORT] == bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) &&
- (ext_csd[EXT_CSD_ERASED_MEM_CONT] == bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) &&
- (ext_csd[EXT_CSD_REV] == bw_ext_csd[EXT_CSD_REV]) &&
- (ext_csd[EXT_CSD_STRUCTURE] == bw_ext_csd[EXT_CSD_STRUCTURE]) &&
- (ext_csd[EXT_CSD_CARD_TYPE] == bw_ext_csd[EXT_CSD_CARD_TYPE]) &&
- (ext_csd[EXT_CSD_S_A_TIMEOUT] == bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) &&
- (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) &&
- (ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] == bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) &&
- (ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] == bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) &&
- (ext_csd[EXT_CSD_SEC_TRIM_MULT] == bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) &&
- (ext_csd[EXT_CSD_SEC_ERASE_MULT] == bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) &&
- (ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] == bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) &&
- (ext_csd[EXT_CSD_TRIM_MULT] == bw_ext_csd[EXT_CSD_TRIM_MULT]) &&
- (ext_csd[EXT_CSD_SEC_CNT + 0] == bw_ext_csd[EXT_CSD_SEC_CNT + 0]) &&
- (ext_csd[EXT_CSD_SEC_CNT + 1] == bw_ext_csd[EXT_CSD_SEC_CNT + 1]) &&
- (ext_csd[EXT_CSD_SEC_CNT + 2] == bw_ext_csd[EXT_CSD_SEC_CNT + 2]) &&
- (ext_csd[EXT_CSD_SEC_CNT + 3] == bw_ext_csd[EXT_CSD_SEC_CNT + 3]) &&
- (ext_csd[EXT_CSD_PWR_CL_52_195] == bw_ext_csd[EXT_CSD_PWR_CL_52_195]) &&
- (ext_csd[EXT_CSD_PWR_CL_26_195] == bw_ext_csd[EXT_CSD_PWR_CL_26_195]) &&
- (ext_csd[EXT_CSD_PWR_CL_52_360] == bw_ext_csd[EXT_CSD_PWR_CL_52_360]) &&
- (ext_csd[EXT_CSD_PWR_CL_26_360] == bw_ext_csd[EXT_CSD_PWR_CL_26_360]) &&
- (ext_csd[EXT_CSD_PWR_CL_200_195] == bw_ext_csd[EXT_CSD_PWR_CL_200_195]) &&
- (ext_csd[EXT_CSD_PWR_CL_200_360] == bw_ext_csd[EXT_CSD_PWR_CL_200_360]) &&
- (ext_csd[EXT_CSD_PWR_CL_DDR_52_195] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) &&
- (ext_csd[EXT_CSD_PWR_CL_DDR_52_360] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]) &&
- (ext_csd[EXT_CSD_PWR_CL_DDR_200_360] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_200_360]));
+ /* only compare read only fields */
+ err = !((ext_csd[EXT_CSD_PARTITION_SUPPORT] == bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) &&
+ (ext_csd[EXT_CSD_ERASED_MEM_CONT] == bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) &&
+ (ext_csd[EXT_CSD_REV] == bw_ext_csd[EXT_CSD_REV]) &&
+ (ext_csd[EXT_CSD_STRUCTURE] == bw_ext_csd[EXT_CSD_STRUCTURE]) &&
+ (ext_csd[EXT_CSD_CARD_TYPE] == bw_ext_csd[EXT_CSD_CARD_TYPE]) &&
+ (ext_csd[EXT_CSD_S_A_TIMEOUT] == bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) &&
+ (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) &&
+ (ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] == bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) &&
+ (ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] == bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) &&
+ (ext_csd[EXT_CSD_SEC_TRIM_MULT] == bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) &&
+ (ext_csd[EXT_CSD_SEC_ERASE_MULT] == bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) &&
+ (ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] == bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) &&
+ (ext_csd[EXT_CSD_TRIM_MULT] == bw_ext_csd[EXT_CSD_TRIM_MULT]) &&
+ (ext_csd[EXT_CSD_SEC_CNT + 0] == bw_ext_csd[EXT_CSD_SEC_CNT + 0]) &&
+ (ext_csd[EXT_CSD_SEC_CNT + 1] == bw_ext_csd[EXT_CSD_SEC_CNT + 1]) &&
+ (ext_csd[EXT_CSD_SEC_CNT + 2] == bw_ext_csd[EXT_CSD_SEC_CNT + 2]) &&
+ (ext_csd[EXT_CSD_SEC_CNT + 3] == bw_ext_csd[EXT_CSD_SEC_CNT + 3]) &&
+ (ext_csd[EXT_CSD_PWR_CL_52_195] == bw_ext_csd[EXT_CSD_PWR_CL_52_195]) &&
+ (ext_csd[EXT_CSD_PWR_CL_26_195] == bw_ext_csd[EXT_CSD_PWR_CL_26_195]) &&
+ (ext_csd[EXT_CSD_PWR_CL_52_360] == bw_ext_csd[EXT_CSD_PWR_CL_52_360]) &&
+ (ext_csd[EXT_CSD_PWR_CL_26_360] == bw_ext_csd[EXT_CSD_PWR_CL_26_360]) &&
+ (ext_csd[EXT_CSD_PWR_CL_200_195] == bw_ext_csd[EXT_CSD_PWR_CL_200_195]) &&
+ (ext_csd[EXT_CSD_PWR_CL_200_360] == bw_ext_csd[EXT_CSD_PWR_CL_200_360]) &&
+ (ext_csd[EXT_CSD_PWR_CL_DDR_52_195] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) &&
+ (ext_csd[EXT_CSD_PWR_CL_DDR_52_360] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]) &&
+ (ext_csd[EXT_CSD_PWR_CL_DDR_200_360] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_200_360]));
- if (err)
- err = -RT_ERROR;
+ if (err)
+ err = -RT_ERROR;
out:
- rt_free(bw_ext_csd);
- return err;
+ rt_free(bw_ext_csd);
+ return err;
}
/*
@@ -289,104 +307,103 @@ out:
*/
static int mmc_select_bus_width(struct rt_mmcsd_card *card, rt_uint8_t *ext_csd)
{
- rt_uint32_t ext_csd_bits[] = {
- EXT_CSD_DDR_BUS_WIDTH_8,
- EXT_CSD_DDR_BUS_WIDTH_4,
- EXT_CSD_BUS_WIDTH_8,
- EXT_CSD_BUS_WIDTH_4,
- EXT_CSD_BUS_WIDTH_1
- };
- rt_uint32_t bus_widths[] = {
- MMCSD_DDR_BUS_WIDTH_8,
- MMCSD_DDR_BUS_WIDTH_4,
- MMCSD_BUS_WIDTH_8,
- MMCSD_BUS_WIDTH_4,
- MMCSD_BUS_WIDTH_1
- };
- struct rt_mmcsd_host *host = card->host;
- unsigned idx, trys, bus_width = 0;
- int err = 0;
+ rt_uint32_t ext_csd_bits[][2] =
+ {
+ {EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8},
+ {EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4},
+ {EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1},
+ };
+ rt_uint32_t bus_widths[] =
+ {
+ MMCSD_BUS_WIDTH_8,
+ MMCSD_BUS_WIDTH_4,
+ MMCSD_BUS_WIDTH_1
+ };
+ struct rt_mmcsd_host *host = card->host;
+ unsigned idx, bus_width = 0;
+ int err = 0, ddr = 0;
- if (GET_BITS(card->resp_csd, 122, 4) < 4)
- return 0;
+ if (GET_BITS(card->resp_csd, 122, 4) < 4)
+ return 0;
- /*
- * Unlike SD, MMC cards dont have a configuration register to notify
- * supported bus width. So bus test command should be run to identify
- * the supported bus width or compare the ext csd values of current
- * bus width and ext csd values of 1 bit mode read earlier.
- */
- for (idx = 0; idx < sizeof(ext_csd_bits)/sizeof(rt_uint32_t); idx++) {
+ if (card->flags & CARD_FLAG_HIGHSPEED_DDR)
+ {
+ ddr = 2;
+ }
/*
- * Host is capable of 8bit transfer, then switch
- * the device to work in 8bit transfer mode. If the
- * mmc switch command returns error then switch to
- * 4bit transfer mode. On success set the corresponding
- * bus width on the host. Meanwhile, mmc core would
- * bail out early if corresponding bus capable wasn't
- * set by drivers.
+ * Unlike SD, MMC cards dont have a configuration register to notify
+ * supported bus width. So bus test command should be run to identify
+ * the supported bus width or compare the ext csd values of current
+ * bus width and ext csd values of 1 bit mode read earlier.
*/
- if ((!((host->flags & MMCSD_BUSWIDTH_8) &&
- (host->flags & MMCSD_SUP_HIGHSPEED_DDR)) &&
- ext_csd_bits[idx] == EXT_CSD_DDR_BUS_WIDTH_8) ||
- (!((host->flags & MMCSD_BUSWIDTH_4) &&
- (host->flags & MMCSD_SUP_HIGHSPEED_DDR)) &&
- ext_csd_bits[idx] == EXT_CSD_DDR_BUS_WIDTH_4) ||
- (!(host->flags & MMCSD_BUSWIDTH_8) &&
- ext_csd_bits[idx] == EXT_CSD_BUS_WIDTH_8) ||
- (!(host->flags & MMCSD_BUSWIDTH_4) &&
- ext_csd_bits[idx] == EXT_CSD_BUS_WIDTH_4))
- continue;
-
- if (host->flags & MMCSD_SUP_HIGHSPEED_DDR)
+ for (idx = 0; idx < sizeof(bus_widths) / sizeof(rt_uint32_t); idx++)
{
- /* HS_TIMING must be set to "0x1" before setting BUS_WIDTH for dual-data-rate(DDR) operation */
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS);
- if (err)
- LOG_E("switch to speed mode width hight speed failed!");
- }
+ /*
+ * Host is capable of 8bit transfer, then switch
+ * the device to work in 8bit transfer mode. If the
+ * mmc switch command returns error then switch to
+ * 4bit transfer mode. On success set the corresponding
+ * bus width on the host. Meanwhile, mmc core would
+ * bail out early if corresponding bus capable wasn't
+ * set by drivers.
+ */
+ bus_width = bus_widths[idx];
+ if (bus_width == MMCSD_BUS_WIDTH_1)
+ {
+ ddr = 0;
+ }
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BUS_WIDTH,
- ext_csd_bits[idx]);
- if (err)
- continue;
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ ext_csd_bits[idx][0]);
+
+ if (err)
+ continue;
- bus_width = bus_widths[idx];
- for(trys = 0; trys < 5; trys++){
mmcsd_set_bus_width(host, bus_width);
- rt_thread_mdelay(10);
err = mmc_compare_ext_csds(card, ext_csd, bus_width);
- if(!err)
+ if (!err)
+ {
break;
+ }
+ else
+ {
+ switch (ext_csd_bits[idx][0])
+ {
+ case 0:
+ LOG_E("switch to bus width 1 bit failed!");
+ break;
+ case 1:
+ LOG_E("switch to bus width 4 bit failed!");
+ break;
+ case 2:
+ LOG_E("switch to bus width 8 bit failed!");
+ break;
+ default:
+ break;
+ }
+ }
}
- if (!err) {
- err = bus_width;
- break;
- } else {
- switch(ext_csd_bits[idx]){
- case EXT_CSD_DDR_BUS_WIDTH_8:
- LOG_E("switch to bus width DDR 8 bit failed!");
- break;
- case EXT_CSD_DDR_BUS_WIDTH_4:
- LOG_E("switch to bus width DDR 4 bit failed!");
- break;
- case EXT_CSD_BUS_WIDTH_8:
- LOG_E("switch to bus width 8 bit failed!");
- break;
- case EXT_CSD_BUS_WIDTH_4:
- LOG_E("switch to bus width 4 bit failed!");
- break;
- case EXT_CSD_BUS_WIDTH_1:
- LOG_E("switch to bus width 1 bit failed!");
- break;
- default:
- break;
- }
+
+ if (!err && ddr)
+ {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH,
+ ext_csd_bits[idx][1]);
}
- }
- return err;
+ if (!err)
+ {
+ if (card->flags & (CARD_FLAG_HIGHSPEED | CARD_FLAG_HIGHSPEED_DDR))
+ {
+
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING,
+ 1);
+ }
+ }
+
+ return err;
}
rt_err_t mmc_send_op_cond(struct rt_mmcsd_host *host,
rt_uint32_t ocr, rt_uint32_t *rocr)
@@ -401,55 +418,98 @@ rt_err_t mmc_send_op_cond(struct rt_mmcsd_host *host,
cmd.arg = controller_is_spi(host) ? 0 : ocr;
cmd.flags = RESP_SPI_R1 | RESP_R3 | CMD_BCR;
- for (i = 100; i; i--) {
- err = mmcsd_send_cmd(host, &cmd, 3);
- if (err)
- break;
+ for (i = 100; i; i--)
+ {
+ err = mmcsd_send_cmd(host, &cmd, 3);
+ if (err)
+ break;
- /* if we're just probing, do a single pass */
- if (ocr == 0)
- break;
+ /* if we're just probing, do a single pass */
+ if (ocr == 0)
+ break;
- /* otherwise wait until reset completes */
- if (controller_is_spi(host)) {
- if (!(cmd.resp[0] & R1_SPI_IDLE))
- break;
- } else {
- if (cmd.resp[0] & CARD_BUSY)
- break;
- }
+ /* otherwise wait until reset completes */
+ if (controller_is_spi(host))
+ {
+ if (!(cmd.resp[0] & R1_SPI_IDLE))
+ break;
+ }
+ else
+ {
+ if (cmd.resp[0] & CARD_BUSY)
+ break;
+ }
- err = -RT_ETIMEOUT;
+ err = -RT_ETIMEOUT;
- rt_thread_mdelay(10); //delay 10ms
+ rt_thread_mdelay(10); //delay 10ms
}
- if (rocr && !controller_is_spi(host))
- *rocr = cmd.resp[0];
+ if (rocr && !controller_is_spi(host))
+ *rocr = cmd.resp[0];
- return err;
+ return err;
}
static rt_err_t mmc_set_card_addr(struct rt_mmcsd_host *host, rt_uint32_t rca)
{
- rt_err_t err;
- struct rt_mmcsd_cmd cmd;
+ rt_err_t err;
+ struct rt_mmcsd_cmd cmd;
- rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd));
+ rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd));
- cmd.cmd_code = SET_RELATIVE_ADDR;
- cmd.arg = rca << 16;
- cmd.flags = RESP_R1 | CMD_AC;
+ cmd.cmd_code = SET_RELATIVE_ADDR;
+ cmd.arg = rca << 16;
+ cmd.flags = RESP_R1 | CMD_AC;
- err = mmcsd_send_cmd(host, &cmd, 3);
- if (err)
- return err;
+ err = mmcsd_send_cmd(host, &cmd, 3);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int mmc_select_hs200(struct rt_mmcsd_card *card)
+{
+ int ret;
- return 0;
+ ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200);
+ if (ret)
+ return ret;
+
+ mmcsd_set_timing(card->host, MMCSD_TIMING_MMC_HS200);
+ mmcsd_set_clock(card->host, 200000000);
+
+ ret = mmcsd_excute_tuning(card);
+
+ return ret;
+}
+
+static int mmc_select_timing(struct rt_mmcsd_card *card)
+{
+ int ret = 0;
+
+ if (card->flags & CARD_FLAG_HS200)
+ {
+ ret = mmc_select_hs200(card);
+ }
+ else if (card->flags & CARD_FLAG_HIGHSPEED_DDR)
+ {
+ mmcsd_set_timing(card->host, MMCSD_TIMING_MMC_DDR52);
+ mmcsd_set_clock(card->host, card->hs_max_data_rate);
+ }
+ else
+ {
+ mmcsd_set_timing(card->host, MMCSD_TIMING_UHS_SDR50);
+ mmcsd_set_clock(card->host, card->hs_max_data_rate);
+ }
+
+ return ret;
}
static rt_int32_t mmcsd_mmc_init_card(struct rt_mmcsd_host *host,
- rt_uint32_t ocr)
+ rt_uint32_t ocr)
{
rt_int32_t err;
rt_uint32_t resp[4];
@@ -463,7 +523,7 @@ static rt_int32_t mmcsd_mmc_init_card(struct rt_mmcsd_host *host,
/* The extra bit indicates that we support high capacity */
err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr);
if (err)
- goto err;
+ goto err;
if (controller_is_spi(host))
{
@@ -526,35 +586,47 @@ static rt_int32_t mmcsd_mmc_init_card(struct rt_mmcsd_host *host,
err = mmc_get_ext_csd(card, &ext_csd);
if (err)
- goto err1;
+ goto err1;
err = mmc_parse_ext_csd(card, ext_csd);
if (err)
- goto err1;
+ goto err1;
/* If doing byte addressing, check if required to do sector
* addressing. Handle the case of <2GB cards needing sector
* addressing. See section 8.1 JEDEC Standard JED84-A441;
* ocr register has bit 30 set for sector addressing.
*/
- if (!(card->flags & CARD_FLAG_SDHC) && (rocr & (1<<30)))
+ if (!(card->flags & CARD_FLAG_SDHC) && (rocr & (1 << 30)))
card->flags |= CARD_FLAG_SDHC;
/* set bus speed */
- if (card->flags & CARD_FLAG_HIGHSPEED)
+ if (card->flags & (CARD_FLAG_HIGHSPEED | CARD_FLAG_HIGHSPEED_DDR))
max_data_rate = card->hs_max_data_rate;
else
max_data_rate = card->max_data_rate;
- mmcsd_set_clock(host, max_data_rate);
+ /*switch bus width and bus mode*/
+ err = mmc_select_bus_width(card, ext_csd);
+ if (err)
+ {
+ LOG_E("mmc select buswidth fail");
+ goto err0;
+ }
- /*switch bus width*/
- mmc_select_bus_width(card, ext_csd);
+ err = mmc_select_timing(card);
+ if (err)
+ {
+ LOG_E("mmc select timing fail");
+ goto err0;
+ }
host->card = card;
rt_free(ext_csd);
return 0;
+err0:
+ rt_free(ext_csd);
err1:
rt_free(card);
err:
diff --git a/components/drivers/sdio/mmcsd_core.c b/components/drivers/sdio/mmcsd_core.c
index 179368026d..f896901804 100644
--- a/components/drivers/sdio/mmcsd_core.c
+++ b/components/drivers/sdio/mmcsd_core.c
@@ -13,6 +13,7 @@
#include <drivers/sd.h>
#include <drivers/mmc.h>
#include <drivers/sdio.h>
+#include <string.h>
#define DBG_TAG "SDIO"
#ifdef RT_SDIO_DEBUG
@@ -40,7 +41,6 @@ static struct rt_mailbox mmcsd_detect_mb;
static rt_uint32_t mmcsd_detect_mb_pool[4];
static struct rt_mailbox mmcsd_hotpluge_mb;
static rt_uint32_t mmcsd_hotpluge_mb_pool[4];
-static rt_uint32_t allocated_host_num = 0;
void mmcsd_host_lock(struct rt_mmcsd_host *host)
{
@@ -59,7 +59,8 @@ void mmcsd_req_complete(struct rt_mmcsd_host *host)
void mmcsd_send_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
{
- do {
+ do
+ {
req->cmd->retries--;
req->cmd->err = 0;
req->cmd->mrq = req;
@@ -79,7 +80,8 @@ void mmcsd_send_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
rt_sem_take(&host->sem_ack, RT_WAITING_FOREVER);
- } while(req->cmd->err && (req->cmd->retries > 0));
+ }
+ while (req->cmd->err && (req->cmd->retries > 0));
}
@@ -243,7 +245,7 @@ rt_int32_t mmcsd_get_cid(struct rt_mmcsd_host *host, rt_uint32_t *cid)
return -RT_ERROR;
}
- for (i = 0;i < 4;i++)
+ for (i = 0; i < 4; i++)
cid[i] = buf[i];
rt_free(buf);
@@ -274,7 +276,7 @@ rt_int32_t mmcsd_get_csd(struct rt_mmcsd_card *card, rt_uint32_t *csd)
return 0;
}
- buf = (rt_uint32_t*)rt_malloc(16);
+ buf = (rt_uint32_t *)rt_malloc(16);
if (!buf)
{
LOG_E("allocate memory failed!");
@@ -320,7 +322,7 @@ rt_int32_t mmcsd_get_csd(struct rt_mmcsd_card *card, rt_uint32_t *csd)
return -RT_ERROR;
}
- for (i = 0;i < 4;i++)
+ for (i = 0; i < 4; i++)
csd[i] = buf[i];
rt_free(buf);
@@ -388,10 +390,10 @@ rt_inline void mmcsd_set_iocfg(struct rt_mmcsd_host *host)
struct rt_mmcsd_io_cfg *io_cfg = &host->io_cfg;
mmcsd_dbg("clock %uHz busmode %u powermode %u cs %u Vdd %u "
- "width %u \n",
- io_cfg->clock, io_cfg->bus_mode,
- io_cfg->power_mode, io_cfg->chip_select, io_cfg->vdd,
- io_cfg->bus_width);
+ "width %u \n",
+ io_cfg->clock, io_cfg->bus_mode,
+ io_cfg->power_mode, io_cfg->chip_select, io_cfg->vdd,
+ io_cfg->bus_width);
host->ops->set_iocfg(host, io_cfg);
}
@@ -438,6 +440,12 @@ void mmcsd_set_bus_width(struct rt_mmcsd_host *host, rt_uint32_t width)
mmcsd_set_iocfg(host);
}
+void mmcsd_set_timing(struct rt_mmcsd_host *host, rt_uint32_t timing)
+{
+ host->io_cfg.timing = timing;
+ mmcsd_set_iocfg(host);
+}
+
void mmcsd_set_data_timeout(struct rt_mmcsd_data *data,
const struct rt_mmcsd_card *card)
{
@@ -475,7 +483,7 @@ void mmcsd_set_data_timeout(struct rt_mmcsd_data *data,
timeout_us = data->timeout_ns / 1000;
timeout_us += data->timeout_clks * 1000 /
- (card->host->io_cfg.clock / 1000);
+ (card->host->io_cfg.clock / 1000);
if (data->flags & DATA_DIR_WRITE)
/*
@@ -596,7 +604,7 @@ int mmcsd_wait_cd_changed(rt_int32_t timeout)
struct rt_mmcsd_host *host;
if (rt_mb_recv(&mmcsd_hotpluge_mb, (rt_ubase_t *)&host, timeout) == RT_EOK)
{
- if(host->card == RT_NULL)
+ if (host->card == RT_NULL)
{
return MMCSD_HOST_UNPLUGED;
}
@@ -667,7 +675,6 @@ void mmcsd_detect(void *param)
continue;
}
mmcsd_host_unlock(host);
- rt_mb_send(&mmcsd_hotpluge_mb, (rt_ubase_t)host);
}
else
{
@@ -691,6 +698,19 @@ void mmcsd_detect(void *param)
}
}
+void mmcsd_host_init(struct rt_mmcsd_host *host)
+{
+ rt_memset(host, 0, sizeof(struct rt_mmcsd_host));
+ strncpy(host->name, "sd", sizeof(host->name) - 1);
+ host->max_seg_size = 65535;
+ host->max_dma_segs = 1;
+ host->max_blk_size = 512;
+ host->max_blk_count = 4096;
+
+ rt_mutex_init(&host->bus_lock, "sd_bus_lock", RT_IPC_FLAG_FIFO);
+ rt_sem_init(&host->sem_ack, "sd_ack", 0, RT_IPC_FLAG_FIFO);
+}
+
struct rt_mmcsd_host *mmcsd_alloc_host(void)
{
struct rt_mmcsd_host *host;
@@ -703,17 +723,7 @@ struct rt_mmcsd_host *mmcsd_alloc_host(void)
return RT_NULL;
}
- rt_memset(host, 0, sizeof(struct rt_mmcsd_host));
-
- host->max_seg_size = 65535;
- host->max_dma_segs = 1;
- host->max_blk_size = 512;
- host->max_blk_count = 4096;
- host->id = allocated_host_num;
- allocated_host_num++;
-
- rt_mutex_init(&host->bus_lock, "sd_bus_lock", RT_IPC_FLAG_PRIO);
- rt_sem_init(&host->sem_ack, "sd_ack", 0, RT_IPC_FLAG_FIFO);
+ mmcsd_host_init(host);
return host;
}
@@ -725,6 +735,22 @@ void mmcsd_free_host(struct rt_mmcsd_host *host)
rt_free(host);
}
+rt_int32_t mmcsd_excute_tuning(struct rt_mmcsd_card *card)
+{
+ struct rt_mmcsd_host *host = card->host;
+ rt_int32_t opcode;
+
+ if (!host->ops->execute_tuning)
+ return RT_EOK;
+
+ if (card->card_type == CARD_TYPE_MMC)
+ opcode = SEND_TUNING_BLOCK_HS200;
+ else
+ opcode = SEND_TUNING_BLOCK;
+
+ return host->ops->execute_tuning(host, opcode);;
+}
+
int rt_mmcsd_core_init(void)
{
rt_err_t ret;
@@ -732,16 +758,16 @@ int rt_mmcsd_core_init(void)
/* initialize detect SD cart thread */
/* initialize mailbox and create detect SD card thread */
ret = rt_mb_init(&mmcsd_detect_mb, "mmcsdmb",
- &mmcsd_detect_mb_pool[0], sizeof(mmcsd_detect_mb_pool) / sizeof(mmcsd_detect_mb_pool[0]),
- RT_IPC_FLAG_FIFO);
+ &mmcsd_detect_mb_pool[0], sizeof(mmcsd_detect_mb_pool) / sizeof(mmcsd_detect_mb_pool[0]),
+ RT_IPC_FLAG_FIFO);
RT_ASSERT(ret == RT_EOK);
- ret = rt_mb_init(&mmcsd_hotpluge_mb, "mmcsdhotplugmb",
- &mmcsd_hotpluge_mb_pool[0], sizeof(mmcsd_hotpluge_mb_pool) / sizeof(mmcsd_hotpluge_mb_pool[0]),
- RT_IPC_FLAG_FIFO);
+ ret = rt_mb_init(&mmcsd_hotpluge_mb, "mmcsdhotplugmb",
+ &mmcsd_hotpluge_mb_pool[0], sizeof(mmcsd_hotpluge_mb_pool) / sizeof(mmcsd_hotpluge_mb_pool[0]),
+ RT_IPC_FLAG_FIFO);
RT_ASSERT(ret == RT_EOK);
- ret = rt_thread_init(&mmcsd_detect_thread, "mmcsd_detect", mmcsd_detect, RT_NULL,
- &mmcsd_stack[0], RT_MMCSD_STACK_SIZE, RT_MMCSD_THREAD_PREORITY, 20);
+ ret = rt_thread_init(&mmcsd_detect_thread, "mmcsd_detect", mmcsd_detect, RT_NULL,
+ &mmcsd_stack[0], RT_MMCSD_STACK_SIZE, RT_MMCSD_THREAD_PREORITY, 20);
if (ret == RT_EOK)
{
rt_thread_startup(&mmcsd_detect_thread);
diff --git a/components/drivers/sdio/sd.c b/components/drivers/sdio/sd.c
index fe63cb86b5..0f2c823235 100644
--- a/components/drivers/sdio/sd.c
+++ b/components/drivers/sdio/sd.c
@@ -119,6 +119,7 @@ static rt_int32_t mmcsd_parse_csd(struct rt_mmcsd_card *card)
card->card_blksize = 512;
card->card_capacity = (csd->c_size + 1) * 512; /* unit:KB */
+ card->card_sec_cnt = card->card_capacity * 2;
card->tacc_clks = 0;
card->tacc_ns = 0;
card->max_data_rate = tran_unit[csd->tran_speed&0x07] * tran_value[(csd->tran_speed&0x78)>>3];
diff --git a/components/drivers/sdio/sdio.c b/components/drivers/sdio/sdio.c
index 7d500c70a5..5c04277549 100644
--- a/components/drivers/sdio/sdio.c
+++ b/components/drivers/sdio/sdio.c
@@ -743,11 +743,7 @@ static rt_int32_t sdio_set_highspeed(struct rt_mmcsd_card *card)
rt_int32_t ret;
rt_uint8_t speed;
- if (!(card->host->flags &
- (MMCSD_SUP_HIGHSPEED |
- MMCSD_SUP_HIGHSPEED_DDR |
- MMCSD_SUP_HIGHSPEED_HS200 |
- MMCSD_SUP_HIGHSPEED_HS400)))
+ if (!(card->host->flags & MMCSD_SUP_HIGHSPEED))
return 0;
if (!card->cccr.high_speed)
diff --git a/components/drivers/serial/serial.c b/components/drivers/serial/serial.c
index 25f06fa401..cce06217b5 100644
--- a/components/drivers/serial/serial.c
+++ b/components/drivers/serial/serial.c
@@ -70,7 +70,7 @@ static int serial_fops_open(struct dfs_fd *fd)
rt_uint16_t flags = 0;
rt_device_t device;
- device = (rt_device_t)fd->data;
+ device = (rt_device_t)fd->vnode->data;
RT_ASSERT(device != RT_NULL);
switch (fd->flags & O_ACCMODE)
@@ -104,7 +104,7 @@ static int serial_fops_close(struct dfs_fd *fd)
{
rt_device_t device;
- device = (rt_device_t)fd->data;
+ device = (rt_device_t)fd->vnode->data;
rt_device_set_rx_indicate(device, RT_NULL);
rt_device_close(device);
@@ -118,7 +118,7 @@ static int serial_fops_ioctl(struct dfs_fd *fd, int cmd, void *args)
int flags = (int)(rt_base_t)args;
int mask = O_NONBLOCK | O_APPEND;
- device = (rt_device_t)fd->data;
+ device = (rt_device_t)fd->vnode->data;
switch (cmd)
{
case FIONREAD:
@@ -139,12 +139,13 @@ static int serial_fops_read(struct dfs_fd *fd, void *buf, size_t count)
{
int size = 0;
rt_device_t device;
+ int wait_ret;
- device = (rt_device_t)fd->data;
+ device = (rt_device_t)fd->vnode->data;
do
{
- size = rt_device_read(device, -1, buf, count);
+ size = rt_device_read(device, -1, buf, count);
if (size <= 0)
{
if (fd->flags & O_NONBLOCK)
@@ -153,10 +154,18 @@ static int serial_fops_read(struct dfs_fd *fd, void *buf, size_t count)
break;
}
- rt_wqueue_wait(&(device->wait_queue), 0, RT_WAITING_FOREVER);
+ wait_ret = rt_wqueue_wait_interruptible(&(device->wait_queue), 0, RT_WAITING_FOREVER);
+ if (wait_ret != RT_EOK)
+ {
+ break;
+ }
}
}while (size <= 0);
+ if (size < 0)
+ {
+ size = 0;
+ }
return size;
}
@@ -164,7 +173,7 @@ static int serial_fops_write(struct dfs_fd *fd, const void *buf, size_t count)
{
rt_device_t device;
- device = (rt_device_t)fd->data;
+ device = (rt_device_t)fd->vnode->data;
return rt_device_write(device, -1, buf, count);
}
@@ -175,7 +184,7 @@ static int serial_fops_poll(struct dfs_fd *fd, struct rt_pollreq *req)
rt_device_t device;
struct rt_serial_device *serial;
- device = (rt_device_t)fd->data;
+ device = (rt_device_t)fd->vnode->data;
RT_ASSERT(device != RT_NULL);
serial = (struct rt_serial_device *)device;
@@ -590,6 +599,8 @@ static rt_err_t rt_serial_init(struct rt_device *dev)
serial->serial_rx = RT_NULL;
serial->serial_tx = RT_NULL;
+ rt_memset(&serial->rx_notify, 0, sizeof(struct rt_device_notify));
+
/* apply configuration */
if (serial->ops->configure)
result = serial->ops->configure(serial, &serial->config);
@@ -1026,7 +1037,19 @@ static rt_err_t rt_serial_control(struct rt_device *dev,
serial->ops->configure(serial, (struct serial_configure *) args);
}
}
+ break;
+ case RT_DEVICE_CTRL_NOTIFY_SET:
+ if (args)
+ {
+ rt_memcpy(&serial->rx_notify, args, sizeof(struct rt_device_notify));
+ }
+ break;
+ case RT_DEVICE_CTRL_CONSOLE_OFLAG:
+ if (args)
+ {
+ *(rt_uint16_t*)args = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM;
+ }
break;
#ifdef RT_USING_POSIX_STDIO
#ifdef RT_USING_POSIX_TERMIOS
@@ -1345,6 +1368,10 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event)
serial->parent.rx_indicate(&serial->parent, rx_length);
}
}
+ if (serial->rx_notify.notify)
+ {
+ serial->rx_notify.notify(serial->rx_notify.dev);
+ }
break;
}
case RT_SERIAL_EVENT_TX_DONE:
diff --git a/components/drivers/serial/serial_v2.c b/components/drivers/serial/serial_v2.c
index f608999f19..fc03605c1d 100644
--- a/components/drivers/serial/serial_v2.c
+++ b/components/drivers/serial/serial_v2.c
@@ -49,7 +49,7 @@ static int serial_fops_open(struct dfs_fd *fd)
rt_uint16_t flags = 0;
rt_device_t device;
- device = (rt_device_t)fd->data;
+ device = (rt_device_t)fd->vnode->data;
RT_ASSERT(device != RT_NULL);
switch (fd->flags & O_ACCMODE)
@@ -83,7 +83,7 @@ static int serial_fops_close(struct dfs_fd *fd)
{
rt_device_t device;
- device = (rt_device_t)fd->data;
+ device = (rt_device_t)fd->vnode->data;
rt_device_set_rx_indicate(device, RT_NULL);
rt_device_close(device);
@@ -97,7 +97,7 @@ static int serial_fops_ioctl(struct dfs_fd *fd, int cmd, void *args)
int flags = (int)(rt_base_t)args;
int mask = O_NONBLOCK | O_APPEND;
- device = (rt_device_t)fd->data;
+ device = (rt_device_t)fd->vnode->data;
switch (cmd)
{
case FIONREAD:
@@ -119,7 +119,7 @@ static int serial_fops_read(struct dfs_fd *fd, void *buf, size_t count)
int size = 0;
rt_device_t device;
- device = (rt_device_t)fd->data;
+ device = (rt_device_t)fd->vnode->data;
do
{
@@ -143,7 +143,7 @@ static int serial_fops_write(struct dfs_fd *fd, const void *buf, size_t count)
{
rt_device_t device;
- device = (rt_device_t)fd->data;
+ device = (rt_device_t)fd->vnode->data;
return rt_device_write(device, -1, buf, count);
}
@@ -154,7 +154,7 @@ static int serial_fops_poll(struct dfs_fd *fd, struct rt_pollreq *req)
rt_device_t device;
struct rt_serial_device *serial;
- device = (rt_device_t)fd->data;
+ device = (rt_device_t)fd->vnode->data;
RT_ASSERT(device != RT_NULL);
serial = (struct rt_serial_device *)device;
@@ -889,6 +889,8 @@ static rt_err_t rt_serial_tx_disable(struct rt_device *dev,
rt_free(tx_fifo);
serial->serial_tx = RT_NULL;
+ rt_memset(&serial->rx_notify, 0, sizeof(struct rt_device_notify));
+
return RT_EOK;
}
@@ -1138,7 +1140,19 @@ static rt_err_t rt_serial_control(struct rt_device *dev,
serial->config = *pconfig;
serial->ops->configure(serial, (struct serial_configure *) args);
}
+ break;
+ case RT_DEVICE_CTRL_NOTIFY_SET:
+ if (args)
+ {
+ rt_memcpy(&serial->rx_notify, args, sizeof(struct rt_device_notify));
+ }
+ break;
+ case RT_DEVICE_CTRL_CONSOLE_OFLAG:
+ if (args)
+ {
+ *(rt_uint16_t*)args = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM;
+ }
break;
#ifdef RT_USING_POSIX_STDIO
#ifdef RT_USING_POSIX_TERMIOS
@@ -1512,6 +1526,11 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event)
/* Trigger the receiving completion callback */
if (serial->parent.rx_indicate != RT_NULL)
serial->parent.rx_indicate(&(serial->parent), rx_length);
+
+ if (serial->rx_notify.notify)
+ {
+ serial->rx_notify.notify(serial->rx_notify.dev);
+ }
break;
}
diff --git a/components/drivers/tty/SConscript b/components/drivers/tty/SConscript
new file mode 100644
index 0000000000..3c561f4e46
--- /dev/null
+++ b/components/drivers/tty/SConscript
@@ -0,0 +1,10 @@
+from building import *
+
+# The set of source files associated with this SConscript file.
+src = Glob('*.c')
+cwd = GetCurrentDir()
+CPPPATH = [cwd + "/include"]
+
+group = DefineGroup('tty', src, depend = ['RT_USING_LWP', 'RT_USING_TTY'], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/components/drivers/tty/console.c b/components/drivers/tty/console.c
new file mode 100644
index 0000000000..dfab484f2e
--- /dev/null
+++ b/components/drivers/tty/console.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021.12.07 linzhenxing first version
+ */
+#include <rtthread.h>
+#include <dfs_file.h>
+#include <dfs_fs.h>
+#include <tty.h>
+
+#define DBG_TAG "CONSOLE"
+#ifdef RT_TTY_DEBUG
+#define DBG_LVL DBG_LOG
+#else
+#define DBG_LVL DBG_INFO
+#endif /* RT_TTY_DEBUG */
+#include <rtdbg.h>
+
+static struct tty_struct console_driver;
+
+static void console_rx_notify(struct rt_device *dev)
+{
+ struct tty_struct *console = NULL;
+ int len = 0;
+ int lens = 0;
+ char ch = 0;
+ char buf[1024] = {0};
+
+ console = (struct tty_struct *)dev;
+ RT_ASSERT(console != RT_NULL);
+
+ while (1)
+ {
+ len = rt_device_read(console->driver, -1, &ch, 1);
+ if (len == 0)
+ {
+ break;
+ }
+ lens += len;
+ buf[lens-1] = ch;
+ if (lens > 1024)
+ {
+ break;
+ }
+ }
+
+ if (console->ldisc->ops->receive_buf)
+ {
+ console->ldisc->ops->receive_buf((struct tty_struct *)console, buf, lens);
+ }
+}
+
+struct tty_struct *console_tty_get(void)
+{
+ return &console_driver;
+}
+
+static void iodev_close(struct tty_struct *console)
+{
+ struct rt_device_notify rx_notify;
+
+ rx_notify.notify = RT_NULL;
+ rx_notify.dev = RT_NULL;
+
+ /* clear notify */
+ rt_device_control(console->driver, RT_DEVICE_CTRL_NOTIFY_SET, &rx_notify);
+ rt_device_close(console->driver);
+}
+
+static rt_err_t iodev_open(struct tty_struct *console)
+{
+ rt_err_t ret = RT_EOK;
+ struct rt_device_notify rx_notify;
+ rt_uint16_t oflags = 0;
+
+ rt_device_control(console->driver, RT_DEVICE_CTRL_CONSOLE_OFLAG, &oflags);
+
+ ret = rt_device_open(console->driver, oflags);
+ if (ret != RT_EOK)
+ {
+ return RT_ERROR;
+ }
+
+ rx_notify.notify = console_rx_notify;
+ rx_notify.dev = (struct rt_device *)console;
+ rt_device_control(console->driver, RT_DEVICE_CTRL_NOTIFY_SET, &rx_notify);
+ return RT_EOK;
+}
+
+struct rt_device *console_get_iodev(void)
+{
+ rt_base_t level = 0;
+ struct rt_device *iodev = RT_NULL;
+
+ level = rt_hw_interrupt_disable();
+ iodev = console_driver.driver;
+ rt_hw_interrupt_enable(level);
+ return iodev;
+}
+
+struct rt_device *console_set_iodev(struct rt_device *iodev)
+{
+ rt_base_t level = 0;
+ struct rt_device *io_before = RT_NULL;
+ struct tty_struct *console = RT_NULL;
+
+ RT_ASSERT(iodev != RT_NULL);
+
+ console = &console_driver;
+
+ level = rt_hw_interrupt_disable();
+
+ RT_ASSERT(console->init_flag >= TTY_INIT_FLAG_REGED);
+
+ io_before = console->driver;
+
+ if (iodev == io_before)
+ {
+ goto exit;
+ }
+
+ if (console->init_flag >= TTY_INIT_FLAG_INITED)
+ {
+ /* close old device */
+ iodev_close(console);
+ }
+
+ console->driver = iodev;
+
+ if (console->init_flag >= TTY_INIT_FLAG_INITED)
+ {
+ rt_err_t ret;
+ /* open new device */
+ ret = iodev_open(console);
+ RT_ASSERT(ret == RT_EOK);
+ }
+
+exit:
+ rt_hw_interrupt_enable(level);
+ return io_before;
+}
+
+/* RT-Thread Device Interface */
+/*
+ * This function initializes console device.
+ */
+static rt_err_t rt_console_init(struct rt_device *dev)
+{
+ rt_base_t level = 0;
+ rt_err_t result = RT_EOK;
+ struct tty_struct *console = RT_NULL;
+
+ RT_ASSERT(dev != RT_NULL);
+
+ console = (struct tty_struct *)dev;
+
+ level = rt_hw_interrupt_disable();
+
+ RT_ASSERT(console->init_flag == TTY_INIT_FLAG_REGED);
+
+ result = iodev_open(console);
+ if (result != RT_EOK)
+ {
+ goto exit;
+ }
+
+ console->init_flag = TTY_INIT_FLAG_INITED;
+exit:
+ rt_hw_interrupt_enable(level);
+ return result;
+}
+
+static rt_err_t rt_console_open(struct rt_device *dev, rt_uint16_t oflag)
+{
+ rt_err_t result = RT_EOK;
+ struct tty_struct *console = RT_NULL;
+
+ RT_ASSERT(dev != RT_NULL);
+ console = (struct tty_struct *)dev;
+ RT_ASSERT(console != RT_NULL);
+ RT_ASSERT(console->init_flag == TTY_INIT_FLAG_INITED);
+ return result;
+}
+
+static rt_err_t rt_console_close(struct rt_device *dev)
+{
+ rt_err_t result = RT_EOK;
+ struct tty_struct *console = RT_NULL;
+
+ console = (struct tty_struct *)dev;
+ RT_ASSERT(console != RT_NULL);
+ RT_ASSERT(console->init_flag == TTY_INIT_FLAG_INITED);
+ return result;
+}
+
+static rt_size_t rt_console_read(struct rt_device *dev,
+ rt_off_t pos,
+ void *buffer,
+ rt_size_t size)
+{
+ rt_size_t len = 0;
+ return len;
+}
+
+static rt_size_t rt_console_write(struct rt_device *dev,
+ rt_off_t pos,
+ const void *buffer,
+ rt_size_t size)
+{
+ rt_size_t len = 0;
+ struct tty_struct *console = RT_NULL;
+
+ console = (struct tty_struct *)dev;
+ RT_ASSERT(console != RT_NULL);
+ RT_ASSERT(console->init_flag == TTY_INIT_FLAG_INITED);
+
+ len = rt_device_write((struct rt_device *)console->driver, -1, buffer, size);
+
+ return len;
+}
+
+static rt_err_t rt_console_control(rt_device_t dev, int cmd, void *args)
+{
+ rt_size_t len = 0;
+ struct tty_struct *console = RT_NULL;
+
+ console = (struct tty_struct *)dev;
+ RT_ASSERT(console != RT_NULL);
+ RT_ASSERT(console->init_flag == TTY_INIT_FLAG_INITED);
+
+ len = rt_device_control((struct rt_device *)console->driver, cmd, args);
+
+ return len;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops console_ops =
+{
+ rt_console_init,
+ rt_console_open,
+ rt_console_close,
+ rt_console_read,
+ rt_console_write,
+ rt_console_control,
+};
+#endif
+/*
+ * console register
+ */
+ static struct dfs_file_ops con_fops;
+rt_err_t console_register(const char *name, struct rt_device *iodev)
+{
+ rt_base_t level = 0;
+ rt_err_t ret = RT_EOK;
+ struct rt_device *device = RT_NULL;
+ struct tty_struct *console = &console_driver;
+
+ level = rt_hw_interrupt_disable();
+ RT_ASSERT(console->init_flag == TTY_INIT_FLAG_NONE);
+ RT_ASSERT(iodev != RT_NULL);
+
+ device = &(console->parent);
+
+ device->type = RT_Device_Class_Char;
+
+#ifdef RT_USING_DEVICE_OPS
+ device->ops = &console_ops;
+#else
+ device->init = rt_console_init;
+ device->open = rt_console_open;
+ device->close = rt_console_close;
+ device->read = rt_console_read;
+ device->write = rt_console_write;
+ device->control = rt_console_control;
+#endif
+
+
+ /* register a character device */
+ ret = rt_device_register(device, name, 0);
+ if (ret != RT_EOK)
+ {
+ LOG_E("console driver register fail\n");
+ goto exit;
+ }
+
+#ifdef RT_USING_POSIX_DEVIO
+ /* set fops */
+ console_set_fops(&con_fops);
+ device->fops = &con_fops;
+#endif
+ console->type = TTY_DRIVER_TYPE_CONSOLE;
+ console->subtype = SERIAL_TYPE_NORMAL;
+ console->driver = iodev;
+ console->head = rt_calloc(1, sizeof(struct tty_node));
+ if (!console->head)
+ {
+ return -RT_ENOMEM;
+ }
+ tty_initstack(console->head);
+
+ rt_mutex_init(&console->lock, "ttylock", RT_IPC_FLAG_PRIO);
+ console->pgrp = -1;
+ console->session = -1;
+ console->foreground = RT_NULL;
+ rt_wqueue_init(&console->wait_queue);
+
+ tty_ldisc_init(console);
+
+extern struct termios tty_std_termios;
+ console->init_termios = tty_std_termios;
+ console->init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL; /* is normally B9600 default... */
+ console->init_termios.__c_ispeed = 9600;
+ console->init_termios.__c_ospeed = 9600;
+
+ console_ldata_init(console);
+ console->init_flag = TTY_INIT_FLAG_REGED;
+exit:
+ rt_hw_interrupt_enable(level);
+ return ret;
+}
diff --git a/components/drivers/tty/include/console.h b/components/drivers/tty/include/console.h
new file mode 100644
index 0000000000..779d816d4b
--- /dev/null
+++ b/components/drivers/tty/include/console.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021.12.07 linzhenxing first version
+ */
+#ifndef __CONSOLE_
+#define __CONSOLE_
+#include <rtthread.h>
+#include "tty.h"
+
+struct tty_struct *console_tty_get(void);
+struct rt_device *console_get_iodev(void);
+struct rt_device *console_set_iodev(struct rt_device *iodev);
+rt_err_t console_register(const char *name, struct rt_device *iodev);
+#endif
diff --git a/components/drivers/tty/include/tty.h b/components/drivers/tty/include/tty.h
new file mode 100644
index 0000000000..2040cc45f3
--- /dev/null
+++ b/components/drivers/tty/include/tty.h
@@ -0,0 +1,490 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021.12.07 linzhenxing first version
+ */
+#ifndef __TTY_H__
+#define __TTY_H__
+#include <rthw.h>
+#include <rtthread.h>
+#include <tty_ldisc.h>
+#ifdef RT_USING_LWP
+#include <lwp.h>
+#endif
+#if defined(RT_USING_POSIX_TERMIOS)
+#include <poll.h>
+#include <termios.h>
+#endif
+
+#ifndef ENOIOCTLCMD
+#define ENOIOCTLCMD (515) /* No ioctl command */
+#endif
+
+#define current lwp_self()
+#define __DISABLED_CHAR '\0'
+
+struct tty_node
+{
+ struct rt_lwp *lwp;
+ struct tty_node *next;
+};
+
+void tty_initstack(struct tty_node *node);
+int tty_push(struct tty_node **head, struct rt_lwp *lwp);
+struct rt_lwp *tty_pop(struct tty_node **head, struct rt_lwp *target_lwp);
+/*
+ * When a break, frame error, or parity error happens, these codes are
+ * stuffed into the flags buffer.
+ */
+#define TTY_NORMAL 0
+#define TTY_BREAK 1
+#define TTY_FRAME 2
+#define TTY_PARITY 3
+#define TTY_OVERRUN 4
+
+#define INTR_CHAR(tty) ((tty)->init_termios.c_cc[VINTR])
+#define QUIT_CHAR(tty) ((tty)->init_termios.c_cc[VQUIT])
+#define ERASE_CHAR(tty) ((tty)->init_termios.c_cc[VERASE])
+#define KILL_CHAR(tty) ((tty)->init_termios.c_cc[VKILL])
+#define EOF_CHAR(tty) ((tty)->init_termios.c_cc[VEOF])
+#define TIME_CHAR(tty) ((tty)->init_termios.c_cc[VTIME])
+#define MIN_CHAR(tty) ((tty)->init_termios.c_cc[VMIN])
+#define SWTC_CHAR(tty) ((tty)->init_termios.c_cc[VSWTC])
+#define START_CHAR(tty) ((tty)->init_termios.c_cc[VSTART])
+#define STOP_CHAR(tty) ((tty)->init_termios.c_cc[VSTOP])
+#define SUSP_CHAR(tty) ((tty)->init_termios.c_cc[VSUSP])
+#define EOL_CHAR(tty) ((tty)->init_termios.c_cc[VEOL])
+#define REPRINT_CHAR(tty) ((tty)->init_termios.c_cc[VREPRINT])
+#define DISCARD_CHAR(tty) ((tty)->init_termios.c_cc[VDISCARD])
+#define WERASE_CHAR(tty) ((tty)->init_termios.c_cc[VWERASE])
+#define LNEXT_CHAR(tty) ((tty)->init_termios.c_cc[VLNEXT])
+#define EOL2_CHAR(tty) ((tty)->init_termios.c_cc[VEOL2])
+
+#define _I_FLAG(tty,f) ((tty)->init_termios.c_iflag & (f))
+#define _O_FLAG(tty,f) ((tty)->init_termios.c_oflag & (f))
+#define _C_FLAG(tty,f) ((tty)->init_termios.c_cflag & (f))
+#define _L_FLAG(tty,f) ((tty)->init_termios.c_lflag & (f))
+
+#define I_IGNBRK(tty) _I_FLAG((tty),IGNBRK)
+#define I_BRKINT(tty) _I_FLAG((tty),BRKINT)
+#define I_IGNPAR(tty) _I_FLAG((tty),IGNPAR)
+#define I_PARMRK(tty) _I_FLAG((tty),PARMRK)
+#define I_INPCK(tty) _I_FLAG((tty),INPCK)
+#define I_ISTRIP(tty) _I_FLAG((tty),ISTRIP)
+#define I_INLCR(tty) _I_FLAG((tty),INLCR)
+#define I_IGNCR(tty) _I_FLAG((tty),IGNCR)
+#define I_ICRNL(tty) _I_FLAG((tty),ICRNL)
+#define I_IUCLC(tty) _I_FLAG((tty),IUCLC)
+#define I_IXON(tty) _I_FLAG((tty),IXON)
+#define I_IXANY(tty) _I_FLAG((tty),IXANY)
+#define I_IXOFF(tty) _I_FLAG((tty),IXOFF)
+#define I_IMAXBEL(tty) _I_FLAG((tty),IMAXBEL)
+#define I_IUTF8(tty) _I_FLAG((tty), IUTF8)
+
+#define O_OPOST(tty) _O_FLAG((tty),OPOST)
+#define O_OLCUC(tty) _O_FLAG((tty),OLCUC)
+#define O_ONLCR(tty) _O_FLAG((tty),ONLCR)
+#define O_OCRNL(tty) _O_FLAG((tty),OCRNL)
+#define O_ONOCR(tty) _O_FLAG((tty),ONOCR)
+#define O_ONLRET(tty) _O_FLAG((tty),ONLRET)
+#define O_OFILL(tty) _O_FLAG((tty),OFILL)
+#define O_OFDEL(tty) _O_FLAG((tty),OFDEL)
+#define O_NLDLY(tty) _O_FLAG((tty),NLDLY)
+#define O_CRDLY(tty) _O_FLAG((tty),CRDLY)
+#define O_TABDLY(tty) _O_FLAG((tty),TABDLY)
+#define O_BSDLY(tty) _O_FLAG((tty),BSDLY)
+#define O_VTDLY(tty) _O_FLAG((tty),VTDLY)
+#define O_FFDLY(tty) _O_FLAG((tty),FFDLY)
+
+#define C_BAUD(tty) _C_FLAG((tty),CBAUD)
+#define C_CSIZE(tty) _C_FLAG((tty),CSIZE)
+#define C_CSTOPB(tty) _C_FLAG((tty),CSTOPB)
+#define C_CREAD(tty) _C_FLAG((tty),CREAD)
+#define C_PARENB(tty) _C_FLAG((tty),PARENB)
+#define C_PARODD(tty) _C_FLAG((tty),PARODD)
+#define C_HUPCL(tty) _C_FLAG((tty),HUPCL)
+#define C_CLOCAL(tty) _C_FLAG((tty),CLOCAL)
+#define C_CIBAUD(tty) _C_FLAG((tty),CIBAUD)
+#define C_CRTSCTS(tty) _C_FLAG((tty),CRTSCTS)
+
+#define L_ISIG(tty) _L_FLAG((tty),ISIG)
+#define L_ICANON(tty) _L_FLAG((tty),ICANON)
+#define L_XCASE(tty) _L_FLAG((tty),XCASE)
+#define L_ECHO(tty) _L_FLAG((tty),ECHO)
+#define L_ECHOE(tty) _L_FLAG((tty),ECHOE)
+#define L_ECHOK(tty) _L_FLAG((tty),ECHOK)
+#define L_ECHONL(tty) _L_FLAG((tty),ECHONL)
+#define L_NOFLSH(tty) _L_FLAG((tty),NOFLSH)
+#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP)
+#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL)
+#define L_ECHOPRT(tty) _L_FLAG((tty),ECHOPRT)
+#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE)
+#define L_FLUSHO(tty) _L_FLAG((tty),FLUSHO)
+#define L_PENDIN(tty) _L_FLAG((tty),PENDIN)
+#define L_IEXTEN(tty) _L_FLAG((tty),IEXTEN)
+#define L_EXTPROC(tty) _L_FLAG((tty), EXTPROC)
+
+/*
+ * Where all of the state associated with a tty is kept while the tty
+ * is open. Since the termios state should be kept even if the tty
+ * has been closed --- for things like the baud rate, etc --- it is
+ * not stored here, but rather a pointer to the real state is stored
+ * here. Possible the winsize structure should have the same
+ * treatment, but (1) the default 80x24 is usually right and (2) it's
+ * most often used by a windowing system, which will set the correct
+ * size each time the window is created or resized anyway.
+ * - TYT, 9/14/92
+ */
+struct tty_struct
+{
+ struct rt_device parent;
+ int type;
+ int subtype;
+ int init_flag;
+ int index; //for pty
+ int pts_lock; //for pty
+
+ struct tty_struct *other_struct; //for pty
+
+ struct termios init_termios;
+ struct winsize winsize;
+ struct rt_mutex lock;
+ pid_t pgrp;
+ pid_t session;
+ struct rt_lwp *foreground;
+ struct tty_node *head;
+
+ struct tty_ldisc *ldisc;
+ void *disc_data;
+ struct rt_device *driver;
+
+ struct rt_wqueue wait_queue;
+
+#define RT_TTY_BUF 1024
+ rt_list_t tty_drivers;
+};
+
+enum
+{
+ TTY_INIT_FLAG_NONE = 0,
+ TTY_INIT_FLAG_ALLOCED,
+ TTY_INIT_FLAG_REGED,
+ TTY_INIT_FLAG_INITED,
+};
+
+#define TTY_DRIVER_TYPE_SYSTEM 0x0001
+#define TTY_DRIVER_TYPE_CONSOLE 0x0002
+#define TTY_DRIVER_TYPE_SERIAL 0x0003
+#define TTY_DRIVER_TYPE_PTY 0x0004
+#define TTY_DRIVER_TYPE_SCC 0x0005 /* scc driver */
+#define TTY_DRIVER_TYPE_SYSCONS 0x0006
+
+/* tty magic number */
+#define TTY_MAGIC 0x5401
+
+/*
+ * These bits are used in the flags field of the tty structure.
+ *
+ * So that interrupts won't be able to mess up the queues,
+ * copy_to_cooked must be atomic with respect to itself, as must
+ * tty->write. Thus, you must use the inline functions set_bit() and
+ * clear_bit() to make things atomic.
+ */
+#define TTY_THROTTLED 0
+#define TTY_IO_ERROR 1
+#define TTY_OTHER_CLOSED 2
+#define TTY_EXCLUSIVE 3
+#define TTY_DEBUG 4
+#define TTY_DO_WRITE_WAKEUP 5
+#define TTY_PUSH 6
+#define TTY_CLOSING 7
+#define TTY_DONT_FLIP 8
+#define TTY_HW_COOK_OUT 14
+#define TTY_HW_COOK_IN 15
+#define TTY_PTY_LOCK 16
+#define TTY_NO_WRITE_SPLIT 17
+
+/*
+ * These bits are used in the flags field of the tty structure.
+ *
+ * So that interrupts won't be able to mess up the queues,
+ * copy_to_cooked must be atomic with respect to itself, as must
+ * tty->write. Thus, you must use the inline functions set_bit() and
+ * clear_bit() to make things atomic.
+ */
+#define TTY_THROTTLED 0
+#define TTY_IO_ERROR 1
+#define TTY_OTHER_CLOSED 2
+#define TTY_EXCLUSIVE 3
+#define TTY_DEBUG 4
+#define TTY_DO_WRITE_WAKEUP 5
+#define TTY_PUSH 6
+#define TTY_CLOSING 7
+#define TTY_DONT_FLIP 8
+#define TTY_HW_COOK_OUT 14
+#define TTY_HW_COOK_IN 15
+#define TTY_PTY_LOCK 16
+#define TTY_NO_WRITE_SPLIT 17
+
+#define NR_LDISCS 30
+
+/* line disciplines */
+#define N_TTY 0
+#define N_SLIP 1
+#define N_MOUSE 2
+#define N_PPP 3
+#define N_STRIP 4
+#define N_AX25 5
+#define N_X25 6 /* X.25 async */
+#define N_6PACK 7
+#define N_MASC 8 /* Reserved for Mobitex module <[email protected]> */
+#define N_R3964 9 /* Reserved for Simatic R3964 module */
+#define N_PROFIBUS_FDL 10 /* Reserved for Profibus */
+#define N_IRDA 11 /* Linux IrDa - http://irda.sourceforge.net/ */
+#define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data */
+ /* cards about SMS messages */
+#define N_HDLC 13 /* synchronous HDLC */
+#define N_SYNC_PPP 14 /* synchronous PPP */
+#define N_HCI 15 /* Bluetooth HCI UART */
+#define N_GIGASET_M101 16 /* Siemens Gigaset M101 serial DECT adapter */
+#define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */
+#define N_PPS 18 /* Pulse per Second */
+#define N_V253 19 /* Codec control over voice modem */
+#define N_CAIF 20 /* CAIF protocol for talking to modems */
+#define N_GSM0710 21 /* GSM 0710 Mux */
+#define N_TI_WL 22 /* for TI's WL BT, FM, GPS combo chips */
+#define N_TRACESINK 23 /* Trace data routing for MIPI P1149.7 */
+#define N_TRACEROUTER 24 /* Trace data routing for MIPI P1149.7 */
+#define N_NCI 25 /* NFC NCI UART */
+
+/* Used for packet mode */
+#define TIOCPKT_DATA 0
+#define TIOCPKT_FLUSHREAD 1
+#define TIOCPKT_FLUSHWRITE 2
+#define TIOCPKT_STOP 4
+#define TIOCPKT_START 8
+#define TIOCPKT_NOSTOP 16
+#define TIOCPKT_DOSTOP 32
+
+/* tty driver types */
+#define TTY_DRIVER_TYPE_SYSTEM 0x0001
+#define TTY_DRIVER_TYPE_CONSOLE 0x0002
+#define TTY_DRIVER_TYPE_SERIAL 0x0003
+#define TTY_DRIVER_TYPE_PTY 0x0004
+#define TTY_DRIVER_TYPE_SCC 0x0005 /* scc driver */
+#define TTY_DRIVER_TYPE_SYSCONS 0x0006
+
+/* pty subtypes */
+#define PTY_TYPE_MASTER 0x0001
+#define PTY_TYPE_SLAVE 0x0002
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL 1
+
+#define max(a, b) ({\
+ typeof(a) _a = a;\
+ typeof(b) _b = b;\
+ _a > _b ? _a : _b; })
+
+#define min(a, b) ({\
+ typeof(a) _a = a;\
+ typeof(b) _b = b;\
+ _a < _b ? _a : _b; })
+
+void tty_set_fops(struct dfs_file_ops *fops);
+void console_set_fops(struct dfs_file_ops *fops);
+void mutex_lock(rt_mutex_t mutex);
+void mutex_unlock(rt_mutex_t mutex);
+int __tty_check_change(struct tty_struct *tty, int sig);
+int tty_check_change(struct tty_struct *tty);
+
+rt_inline struct rt_wqueue *wait_queue_get(struct rt_lwp *lwp, struct tty_struct *tty)
+{
+ if (lwp == RT_NULL)
+ {
+ return &tty->wait_queue;
+ }
+ return &lwp->wait_queue;
+}
+
+rt_inline struct rt_wqueue *wait_queue_current_get(struct rt_lwp *lwp, struct tty_struct *tty)
+{
+ return wait_queue_get(lwp, tty);
+}
+
+rt_inline void tty_wakeup_check(struct tty_struct *tty)
+{
+ struct rt_wqueue *wq = NULL;
+
+ wq = wait_queue_current_get(tty->foreground, tty);
+ rt_wqueue_wakeup(wq, (void*)POLLIN);
+}
+
+rt_inline int set_bit(int nr,int *addr)
+{
+ int mask, retval, level;
+
+ addr += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ level = rt_hw_interrupt_disable();
+ retval = (mask & *addr) != 0;
+ *addr |= mask;
+ rt_hw_interrupt_enable(level);
+ return retval;
+}
+
+rt_inline int clear_bit(int nr, int *addr)
+{
+ int mask, retval, level;
+
+ addr += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ level = rt_hw_interrupt_disable();
+ retval = (mask & *addr) != 0;
+ *addr &= ~mask;
+ rt_hw_interrupt_enable(level);
+ return retval;
+}
+
+rt_inline int test_bit(int nr, int *addr)
+{
+ int mask;
+
+ addr += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ return ((mask & *addr) != 0);
+}
+
+rt_inline int test_and_clear_bit(int nr, volatile void *addr)
+{
+ int mask, retval, level;
+ volatile unsigned int *a = addr;
+
+ a += nr >> 5;
+ mask = 1 << (nr & 0x1f);
+ level = rt_hw_interrupt_disable();
+ retval = (mask & *a) != 0;
+ *a &= ~mask;
+ rt_hw_interrupt_enable(level);
+
+ return retval;
+}
+
+rt_inline unsigned long __ffs(unsigned long word)
+{
+ int num = 0;
+
+#if BITS_PER_LONG == 64
+ if ((word & 0xffffffff) == 0)
+ {
+ num += 32;
+ word >>= 32;
+ }
+#endif
+ if ((word & 0xffff) == 0)
+ {
+ num += 16;
+ word >>= 16;
+ }
+ if ((word & 0xff) == 0)
+ {
+ num += 8;
+ word >>= 8;
+ }
+ if ((word & 0xf) == 0)
+ {
+ num += 4;
+ word >>= 4;
+ }
+ if ((word & 0x3) == 0)
+ {
+ num += 2;
+ word >>= 2;
+ }
+ if ((word & 0x1) == 0)
+ {
+ num += 1;
+ }
+
+ return num;
+}
+#define BITS_PER_LONG 32
+#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG)
+
+/*
+ * Find the next set bit in a memory region.
+ */
+rt_inline unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
+ unsigned long offset)
+{
+ const unsigned long *p = addr + BITOP_WORD(offset);
+ unsigned long result = offset & ~(BITS_PER_LONG-1);
+ unsigned long tmp;
+
+ if (offset >= size)
+ {
+ return size;
+ }
+
+ size -= result;
+ offset %= BITS_PER_LONG;
+ if (offset)
+ {
+ tmp = *(p++);
+ tmp &= (~0UL << offset);
+ if (size < BITS_PER_LONG)
+ {
+ goto found_first;
+ }
+
+ if (tmp)
+ {
+ goto found_middle;
+ }
+
+ size -= BITS_PER_LONG;
+ result += BITS_PER_LONG;
+ }
+
+ while (size & ~(BITS_PER_LONG-1))
+ {
+ if ((tmp = *(p++)))
+ {
+ goto found_middle;
+ }
+
+ result += BITS_PER_LONG;
+ size -= BITS_PER_LONG;
+ }
+
+ if (!size)
+ {
+ return result;
+ }
+
+ tmp = *p;
+
+found_first:
+ tmp &= (~0UL >> (BITS_PER_LONG - size));
+ if (tmp == 0UL) /* Are any bits set? */
+ {
+ return result + size; /* Nope. */
+ }
+
+found_middle:
+ return result + __ffs(tmp);
+}
+
+/*create by tty_ioctl.c*/
+int n_tty_ioctl_extend(struct tty_struct *tty, int cmd, void *arg);
+
+/*create by n_tty.c*/
+void console_ldata_init(struct tty_struct *tty);
+int n_tty_receive_buf(struct tty_struct *tty, char *cp, int count);
+void n_tty_init(void);
+
+#endif /*__TTY_H__*/
diff --git a/components/drivers/tty/include/tty_ldisc.h b/components/drivers/tty/include/tty_ldisc.h
new file mode 100644
index 0000000000..919805f645
--- /dev/null
+++ b/components/drivers/tty/include/tty_ldisc.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021.12.07 linzhenxing first version
+ */
+#ifndef __TTY_LDISC_
+#define __TTY_LDISC_
+#include <rtthread.h>
+#include <dfs.h>
+#include <dfs_fs.h>
+#include <tty.h>
+#if defined(RT_USING_POSIX_TERMIOS)
+#include <termios.h>
+#endif
+struct tty_struct;
+
+struct tty_ldisc_ops
+{
+ char *name;
+ int num;
+
+ int (*open) (struct dfs_fd *fd);
+ int (*close) (struct tty_struct *tty);
+ int (*ioctl) (struct dfs_fd *fd, int cmd, void *args);
+ int (*read) (struct dfs_fd *fd, void *buf, size_t count);
+ int (*write) (struct dfs_fd *fd, const void *buf, size_t count);
+ int (*flush) (struct dfs_fd *fd);
+ int (*lseek) (struct dfs_fd *fd, off_t offset);
+ int (*getdents) (struct dfs_fd *fd, struct dirent *dirp, uint32_t count);
+
+ int (*poll) (struct dfs_fd *fd, struct rt_pollreq *req);
+ void (*set_termios) (struct tty_struct *tty, struct termios *old);
+ int (*receive_buf) (struct tty_struct *tty,char *cp, int count);
+
+ int refcount;
+};
+
+struct tty_ldisc
+{
+ struct tty_ldisc_ops *ops;
+ struct tty_struct *tty;
+};
+
+#define TTY_LDISC_MAGIC 0x5403
+
+int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc);
+void tty_ldisc_kill(struct tty_struct *tty);
+void tty_ldisc_init(struct tty_struct *tty);
+void tty_ldisc_release(struct tty_struct *tty);
+void n_tty_init(void);
+
+#endif // __TTY_LDISC_
diff --git a/components/drivers/tty/n_tty.c b/components/drivers/tty/n_tty.c
new file mode 100644
index 0000000000..658c6c0d9e
--- /dev/null
+++ b/components/drivers/tty/n_tty.c
@@ -0,0 +1,2041 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021.12.07 linzhenxing first version
+ */
+#include <stddef.h>
+#include <ctype.h>
+#include <tty.h>
+#include <lwp.h>
+#if defined(RT_USING_POSIX_TERMIOS)
+#include <termios.h>
+#endif
+#include <sys/errno.h>
+
+#define DBG_TAG "N_TTY"
+#ifdef RT_TTY_DEBUG
+#define DBG_LVL DBG_LOG
+#else
+#define DBG_LVL DBG_INFO
+#endif /* RT_TTY_DEBUG */
+#include <rtdbg.h>
+
+/* number of characters left in xmit buffer before select has we have room */
+#define WAKEUP_CHARS 256
+
+/*
+ * This defines the low- and high-watermarks for throttling and
+ * unthrottling the TTY driver. These watermarks are used for
+ * controlling the space in the read buffer.
+ */
+#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */
+#define TTY_THRESHOLD_UNTHROTTLE 128
+
+/*
+ * Special byte codes used in the echo buffer to represent operations
+ * or special handling of characters. Bytes in the echo buffer that
+ * are not part of such special blocks are treated as normal character
+ * codes.
+ */
+#define ECHO_OP_START 0xff
+#define ECHO_OP_MOVE_BACK_COL 0x80
+#define ECHO_OP_SET_CANON_COL 0x81
+#define ECHO_OP_ERASE_TAB 0x82
+
+#define ECHO_COMMIT_WATERMARK 256
+#define ECHO_BLOCK 256
+#define ECHO_DISCARD_WATERMARK RT_TTY_BUF - (ECHO_BLOCK + 32)
+
+rt_inline void tty_sigaddset(lwp_sigset_t *set, int _sig)
+{
+ unsigned long sig = _sig - 1;
+
+ if (_LWP_NSIG_WORDS == 1)
+ {
+ set->sig[0] |= 1UL << sig;
+ }
+ else
+ {
+ set->sig[sig / _LWP_NSIG_BPW] |= 1UL << (sig % _LWP_NSIG_BPW);
+ }
+}
+
+struct n_tty_data
+{
+ /* producer-published */
+ size_t read_head;
+ size_t commit_head;
+ size_t canon_head;
+ size_t echo_head;
+ size_t echo_commit;
+ size_t echo_mark;
+ unsigned long char_map[256];
+
+ /* non-atomic */
+ rt_bool_t no_room;
+ /* must hold exclusive termios_rwsem to reset these */
+ unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
+ unsigned char push:1;
+
+ /* shared by producer and consumer */
+ char read_buf[RT_TTY_BUF];
+ unsigned long read_flags[RT_TTY_BUF];
+ unsigned char echo_buf[RT_TTY_BUF];
+
+ /* consumer-published */
+ size_t read_tail;
+ size_t line_start;
+
+ /* protected by output lock */
+ unsigned int column;
+ unsigned int canon_column;
+ size_t echo_tail;
+
+ rt_mutex_t atomic_read_lock;
+ rt_mutex_t output_lock;
+};
+
+static inline size_t read_cnt(struct n_tty_data *ldata)
+{
+ return ldata->read_head - ldata->read_tail;
+}
+
+static inline char read_buf(struct n_tty_data *ldata, size_t i)
+{
+ return ldata->read_buf[i & (RT_TTY_BUF - 1)];
+}
+
+static inline char *read_buf_addr(struct n_tty_data *ldata, size_t i)
+{
+ return &ldata->read_buf[i & (RT_TTY_BUF - 1)];
+}
+
+static inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i)
+{
+ return ldata->echo_buf[i & (RT_TTY_BUF - 1)];
+}
+
+static inline unsigned char *echo_buf_addr(struct n_tty_data *ldata, size_t i)
+{
+ return &ldata->echo_buf[i & (RT_TTY_BUF - 1)];
+}
+
+/**
+ * put_tty_queue - add character to tty
+ * @c: character
+ * @ldata: n_tty data
+ *
+ * Add a character to the tty read_buf queue.
+ *
+ * n_tty_receive_buf()/producer path:
+ * caller holds non-exclusive termios_rwsem
+ */
+
+static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata)
+{
+ *read_buf_addr(ldata, ldata->read_head) = c;
+ ldata->read_head++;
+}
+
+/**
+ * reset_buffer_flags - reset buffer state
+ * @tty: terminal to reset
+ *
+ * Reset the read buffer counters and clear the flags.
+ * Called from n_tty_open() and n_tty_flush_buffer().
+ *
+ * Locking: caller holds exclusive termios_rwsem
+ * (or locking is not required)
+ */
+
+static void reset_buffer_flags(struct n_tty_data *ldata)
+{
+ ldata->read_head = ldata->canon_head = ldata->read_tail = 0;
+ ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0;
+ ldata->commit_head = 0;
+ ldata->echo_mark = 0;
+ ldata->line_start = 0;
+
+ ldata->erasing = 0;
+ rt_memset(ldata->read_flags, 0, RT_TTY_BUF);
+ ldata->push = 0;
+}
+
+/**
+ * add_echo_byte - add a byte to the echo buffer
+ * @c: unicode byte to echo
+ * @ldata: n_tty data
+ *
+ * Add a character or operation byte to the echo buffer.
+ */
+
+static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata)
+{
+ *echo_buf_addr(ldata, ldata->echo_head++) = c;
+}
+
+/**
+ * echo_move_back_col - add operation to move back a column
+ * @ldata: n_tty data
+ *
+ * Add an operation to the echo buffer to move back one column.
+ */
+
+static void echo_move_back_col(struct n_tty_data *ldata)
+{
+ add_echo_byte(ECHO_OP_START, ldata);
+ add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata);
+}
+
+/**
+ * echo_set_canon_col - add operation to set the canon column
+ * @ldata: n_tty data
+ *
+ * Add an operation to the echo buffer to set the canon column
+ * to the current column.
+ */
+
+static void echo_set_canon_col(struct n_tty_data *ldata)
+{
+ add_echo_byte(ECHO_OP_START, ldata);
+ add_echo_byte(ECHO_OP_SET_CANON_COL, ldata);
+}
+
+/**
+ * echo_erase_tab - add operation to erase a tab
+ * @num_chars: number of character columns already used
+ * @after_tab: true if num_chars starts after a previous tab
+ * @ldata: n_tty data
+ *
+ * Add an operation to the echo buffer to erase a tab.
+ *
+ * Called by the eraser function, which knows how many character
+ * columns have been used since either a previous tab or the start
+ * of input. This information will be used later, along with
+ * canon column (if applicable), to go back the correct number
+ * of columns.
+ */
+
+static void echo_erase_tab(unsigned int num_chars, int after_tab,
+ struct n_tty_data *ldata)
+{
+ add_echo_byte(ECHO_OP_START, ldata);
+ add_echo_byte(ECHO_OP_ERASE_TAB, ldata);
+
+ /* We only need to know this modulo 8 (tab spacing) */
+ num_chars &= 7;
+
+ /* Set the high bit as a flag if num_chars is after a previous tab */
+ if (after_tab)
+ {
+ num_chars |= 0x80;
+ }
+
+ add_echo_byte(num_chars, ldata);
+}
+
+/**
+ * echo_char_raw - echo a character raw
+ * @c: unicode byte to echo
+ * @tty: terminal device
+ *
+ * Echo user input back onto the screen. This must be called only when
+ * L_ECHO(tty) is true. Called from the driver receive_buf path.
+ *
+ * This variant does not treat control characters specially.
+ */
+
+static void echo_char_raw(unsigned char c, struct n_tty_data *ldata)
+{
+ if (c == ECHO_OP_START)
+ {
+ add_echo_byte(ECHO_OP_START, ldata);
+ add_echo_byte(ECHO_OP_START, ldata);
+ }
+ else
+ {
+ add_echo_byte(c, ldata);
+ }
+}
+
+/**
+ * echo_char - echo a character
+ * @c: unicode byte to echo
+ * @tty: terminal device
+ *
+ * Echo user input back onto the screen. This must be called only when
+ * L_ECHO(tty) is true. Called from the driver receive_buf path.
+ *
+ * This variant tags control characters to be echoed as "^X"
+ * (where X is the letter representing the control char).
+ */
+
+static void echo_char(unsigned char c, struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ if (c == ECHO_OP_START)
+ {
+ add_echo_byte(ECHO_OP_START, ldata);
+ add_echo_byte(ECHO_OP_START, ldata);
+ }
+ else
+ {
+ if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t')
+ {
+ add_echo_byte(ECHO_OP_START, ldata);
+ }
+ add_echo_byte(c, ldata);
+ }
+}
+
+/**
+ * finish_erasing - complete erase
+ * @ldata: n_tty data
+ */
+
+static inline void finish_erasing(struct n_tty_data *ldata)
+{
+ if (ldata->erasing)
+ {
+ echo_char_raw('/', ldata);
+ ldata->erasing = 0;
+ }
+}
+
+/**
+ * is_utf8_continuation - utf8 multibyte check
+ * @c: byte to check
+ *
+ * Returns true if the utf8 character 'c' is a multibyte continuation
+ * character. We use this to correctly compute the on screen size
+ * of the character when printing
+ */
+
+static inline int is_utf8_continuation(unsigned char c)
+{
+ return (c & 0xc0) == 0x80;
+}
+
+/**
+ * is_continuation - multibyte check
+ * @c: byte to check
+ *
+ * Returns true if the utf8 character 'c' is a multibyte continuation
+ * character and the terminal is in unicode mode.
+ */
+
+static inline int is_continuation(unsigned char c, struct tty_struct *tty)
+{
+ return I_IUTF8(tty) && is_utf8_continuation(c);
+}
+
+/**
+ * eraser - handle erase function
+ * @c: character input
+ * @tty: terminal device
+ *
+ * Perform erase and necessary output when an erase character is
+ * present in the stream from the driver layer. Handles the complexities
+ * of UTF-8 multibyte symbols.
+ *
+ * n_tty_receive_buf()/producer path:
+ * caller holds non-exclusive termios_rwsem
+ */
+
+static void eraser(unsigned char c, struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ enum { KERASE, WERASE, KILL } kill_type;
+ size_t head = 0;
+ size_t cnt = 0;
+ int seen_alnums = 0;
+
+ if (ldata->read_head == ldata->canon_head)
+ {
+ /* process_output('\a', tty); */ /* what do you think? */
+ return;
+ }
+
+ if (c == ERASE_CHAR(tty))
+ {
+ kill_type = KERASE;
+ }
+ else if (c == WERASE_CHAR(tty))
+ {
+ kill_type = WERASE;
+ }
+ else
+ {
+ if (!L_ECHO(tty))
+ {
+ ldata->read_head = ldata->canon_head;
+ return;
+ }
+
+ if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty))
+ {
+ ldata->read_head = ldata->canon_head;
+ finish_erasing(ldata);
+ echo_char(KILL_CHAR(tty), tty);
+ /* Add a newline if ECHOK is on and ECHOKE is off. */
+ if (L_ECHOK(tty))
+ {
+ echo_char_raw('\n', ldata);
+ }
+ return;
+ }
+ kill_type = KILL;
+ }
+
+ seen_alnums = 0;
+ while (ldata->read_head != ldata->canon_head)
+ {
+ head = ldata->read_head;
+
+ /* erase a single possibly multibyte character */
+ do
+ {
+ head--;
+ c = read_buf(ldata, head);
+ } while (is_continuation(c, tty) && head != ldata->canon_head);
+
+ /* do not partially erase */
+ if (is_continuation(c, tty))
+ {
+ break;
+ }
+
+ if (kill_type == WERASE)
+ {
+ /* Equivalent to BSD's ALTWERASE. */
+ if (isalnum(c) || c == '_')
+ {
+ seen_alnums++;
+ }
+ else if (seen_alnums)
+ {
+ break;
+ }
+ }
+ cnt = ldata->read_head - head;
+ ldata->read_head = head;
+ if (L_ECHO(tty))
+ {
+ if (L_ECHOPRT(tty))
+ {
+ if (!ldata->erasing)
+ {
+ echo_char_raw('\\', ldata);
+ ldata->erasing = 1;
+ }
+ /* if cnt > 1, output a multi-byte character */
+ echo_char(c, tty);
+ while (--cnt > 0)
+ {
+ head++;
+ echo_char_raw(read_buf(ldata, head), ldata);
+ echo_move_back_col(ldata);
+ }
+ }
+ else if (kill_type == KERASE && !L_ECHOE(tty))
+ {
+ echo_char(ERASE_CHAR(tty), tty);
+ }
+ else if (c == '\t')
+ {
+ unsigned int num_chars = 0;
+ int after_tab = 0;
+ size_t tail = ldata->read_head;
+
+ /*
+ * Count the columns used for characters
+ * since the start of input or after a
+ * previous tab.
+ * This info is used to go back the correct
+ * number of columns.
+ */
+ while (tail != ldata->canon_head)
+ {
+ tail--;
+ c = read_buf(ldata, tail);
+ if (c == '\t')
+ {
+ after_tab = 1;
+ break;
+ }
+ else if (iscntrl(c))
+ {
+ if (L_ECHOCTL(tty))
+ {
+ num_chars += 2;
+ }
+ }
+ else if (!is_continuation(c, tty))
+ {
+ num_chars++;
+ }
+ }
+ echo_erase_tab(num_chars, after_tab, ldata);
+ }
+ else
+ {
+ if (iscntrl(c) && L_ECHOCTL(tty))
+ {
+ echo_char_raw('\b', ldata);
+ echo_char_raw(' ', ldata);
+ echo_char_raw('\b', ldata);
+ }
+ if (!iscntrl(c) || L_ECHOCTL(tty))
+ {
+ echo_char_raw('\b', ldata);
+ echo_char_raw(' ', ldata);
+ echo_char_raw('\b', ldata);
+ }
+ }
+ }
+ if (kill_type == KERASE)
+ {
+ break;
+ }
+ }
+ if (ldata->read_head == ldata->canon_head && L_ECHO(tty))
+ {
+ finish_erasing(ldata);
+ }
+}
+
+/**
+ * isig - handle the ISIG optio
+ * @sig: signal
+ * @tty: terminal
+ *
+ * Called when a signal is being sent due to terminal input.
+ * Called from the driver receive_buf path so serialized.
+ *
+ * Performs input and output flush if !NOFLSH. In this context, the echo
+ * buffer is 'output'. The signal is processed first to alert any current
+ * readers or writers to discontinue and exit their i/o loops.
+ *
+ * Locking: ctrl_lock
+ */
+
+static void __isig(int sig, struct tty_struct *tty)
+{
+ struct rt_lwp *lwp = tty->foreground;
+ struct tty_ldisc *ld = RT_NULL;
+ struct termios old_termios;
+ struct termios *new_termios = get_old_termios();
+
+ if (lwp)
+ {
+ if (sig == SIGTSTP)
+ {
+ struct rt_lwp *old_lwp;
+
+ rt_memcpy(&old_termios, &(tty->init_termios), sizeof(struct termios));
+ tty->init_termios = *new_termios;
+ ld = tty->ldisc;
+ if (ld != RT_NULL)
+ {
+ if (ld->ops->set_termios)
+ {
+ ld->ops->set_termios(tty, &old_termios);
+ }
+ }
+ tty_sigaddset(&lwp->signal_mask, SIGTTOU);
+ old_lwp = tty_pop(&tty->head, RT_NULL);
+ tty->foreground = old_lwp;
+ }
+ else
+ {
+ lwp_kill(lwp_to_pid(lwp), sig);
+ }
+ }
+}
+
+static void isig(int sig, struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ if (L_NOFLSH(tty))
+ {
+ /* signal only */
+ __isig(sig, tty);
+
+ }
+ else
+ { /* signal and flush */
+ __isig(sig, tty);
+
+ /* clear echo buffer */
+ ldata->echo_head = ldata->echo_tail = 0;
+ ldata->echo_mark = ldata->echo_commit = 0;
+
+ /* clear input buffer */
+ reset_buffer_flags(tty->disc_data);
+ }
+}
+
+/**
+ * do_output_char - output one character
+ * @c: character (or partial unicode symbol)
+ * @tty: terminal device
+ * @space: space available in tty driver write buffer
+ *
+ * This is a helper function that handles one output character
+ * (including special characters like TAB, CR, LF, etc.),
+ * doing OPOST processing and putting the results in the
+ * tty driver's write buffer.
+ *
+ * Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY
+ * and NLDLY. They simply aren't relevant in the world today.
+ * If you ever need them, add them here.
+ *
+ * Returns the number of bytes of buffer space used or -1 if
+ * no space left.
+ *
+ * Locking: should be called under the output_lock to protect
+ * the column state and space left in the buffer
+ */
+
+static int do_output_char(unsigned char c, struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ int spaces = 0;
+ char *ch = RT_NULL;
+
+ switch (c)
+ {
+ case '\n':
+ if (O_ONLRET(tty))
+ {
+ ldata->column = 0;
+ }
+
+ if (O_ONLCR(tty))
+ {
+ ldata->canon_column = ldata->column = 0;
+ ch = "\r\n";
+ rt_device_write((rt_device_t)tty, -1, ch, 2);
+ return 2;
+ }
+ ldata->canon_column = ldata->column;
+ break;
+ case '\r':
+ if (O_ONOCR(tty) && ldata->column == 0)
+ {
+ return 0;
+ }
+
+ if (O_OCRNL(tty))
+ {
+ c = '\n';
+ if (O_ONLRET(tty))
+ {
+ ldata->canon_column = ldata->column = 0;
+ }
+ break;
+ }
+ ldata->canon_column = ldata->column = 0;
+ break;
+ case '\t':
+ spaces = 8 - (ldata->column & 7);
+ if (O_TABDLY(tty) == XTABS)
+ {
+ ldata->column += spaces;
+ ch = " ";
+ rt_device_write((rt_device_t)tty, -1, &ch, spaces);
+ return spaces;
+ }
+ ldata->column += spaces;
+ break;
+ case '\b':
+ if (ldata->column > 0)
+ {
+ ldata->column--;
+ }
+ ch = "\b \b";
+ rt_device_write((rt_device_t)tty, -1, ch, strlen(ch));
+ return 1;
+ default:
+ if (!iscntrl(c))
+ {
+ if (O_OLCUC(tty))
+ {
+ c = toupper(c);
+ }
+
+ if (!is_continuation(c, tty))
+ {
+ ldata->column++;
+ }
+ }
+ break;
+ }
+
+ rt_device_write((rt_device_t)tty, -1, &c, 1);
+ return 1;
+}
+
+/**
+ * process_output - output post processor
+ * @c: character (or partial unicode symbol)
+ * @tty: terminal device
+ *
+ * Output one character with OPOST processing.
+ * Returns -1 when the output device is full and the character
+ * must be retried.
+ *
+ * Locking: output_lock to protect column state and space left
+ * (also, this is called from n_tty_write under the
+ * tty layer write lock)
+ */
+
+static int process_output(unsigned char c, struct tty_struct *tty)
+{
+ int retval = 0;
+
+ retval = do_output_char(c, tty);
+ if (retval < 0)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/**
+ * process_output_block - block post processor
+ * @tty: terminal device
+ * @buf: character buffer
+ * @nr: number of bytes to output
+ *
+ * Output a block of characters with OPOST processing.
+ * Returns the number of characters output.
+ *
+ * This path is used to speed up block console writes, among other
+ * things when processing blocks of output data. It handles only
+ * the simple cases normally found and helps to generate blocks of
+ * symbols for the console driver and thus improve performance.
+ *
+ * Locking: output_lock to protect column state and space left
+ * (also, this is called from n_tty_write under the
+ * tty layer write lock)
+ */
+
+static ssize_t process_output_block(struct tty_struct *tty,
+ const char *buf, unsigned int nr)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ int i = 0;
+ ssize_t size = 0;
+ const char *cp = RT_NULL;
+
+ for (i = 0, cp = buf; i < nr; i++, cp++)
+ {
+ char c = *cp;
+
+ switch (c)
+ {
+ case '\n':
+ if (O_ONLRET(tty))
+ {
+ ldata->column = 0;
+ }
+
+ if (O_ONLCR(tty))
+ {
+ goto break_out;
+ }
+ ldata->canon_column = ldata->column;
+ break;
+ case '\r':
+ if (O_ONOCR(tty) && ldata->column == 0)
+ {
+ goto break_out;
+ }
+
+ if (O_OCRNL(tty))
+ {
+ goto break_out;
+ }
+
+ ldata->canon_column = ldata->column = 0;
+ break;
+ case '\t':
+ goto break_out;
+ case '\b':
+ if (ldata->column > 0)
+ {
+ ldata->column--;
+ }
+ break;
+ default:
+ if (!iscntrl(c))
+ {
+ if (O_OLCUC(tty))
+ {
+ goto break_out;
+ }
+
+ if (!is_continuation(c, tty))
+ {
+ ldata->column++;
+ }
+ }
+ break;
+ }
+ }
+break_out:
+ size = rt_device_write((rt_device_t)tty, -1, buf, i);
+ return size;
+}
+
+static size_t __process_echoes(struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ size_t tail = 0;
+ unsigned char c = 0;
+ char ch = 0;
+ unsigned char num_chars = 0, num_bs = 0;
+
+ tail = ldata->echo_tail;
+ while (ldata->echo_commit != tail)
+ {
+ c = echo_buf(ldata, tail);
+ if (c == ECHO_OP_START)
+ {
+ unsigned char op = 0;
+
+ /*
+ * If the buffer byte is the start of a multi-byte
+ * operation, get the next byte, which is either the
+ * op code or a control character value.
+ */
+ op = echo_buf(ldata, tail + 1);
+
+ switch (op)
+ {
+ case ECHO_OP_ERASE_TAB:
+ num_chars = echo_buf(ldata, tail + 2);
+
+ /*
+ * Determine how many columns to go back
+ * in order to erase the tab.
+ * This depends on the number of columns
+ * used by other characters within the tab
+ * area. If this (modulo 8) count is from
+ * the start of input rather than from a
+ * previous tab, we offset by canon column.
+ * Otherwise, tab spacing is normal.
+ */
+ if (!(num_chars & 0x80))
+ {
+ num_chars += ldata->canon_column;
+ }
+ num_bs = 8 - (num_chars & 7);
+
+ while (num_bs--)
+ {
+ ch = '\b';
+ rt_device_write((rt_device_t)tty, -1, &ch, 1);
+ if (ldata->column > 0)
+ {
+ ldata->column--;
+ }
+ }
+ tail += 3;
+ break;
+
+ case ECHO_OP_SET_CANON_COL:
+ ldata->canon_column = ldata->column;
+ tail += 2;
+ break;
+
+ case ECHO_OP_MOVE_BACK_COL:
+ if (ldata->column > 0)
+ {
+ ldata->column--;
+ }
+ tail += 2;
+ break;
+
+ case ECHO_OP_START:
+ ch = ECHO_OP_START;
+ rt_device_write((rt_device_t)tty, -1, &ch, 1);
+ ldata->column++;
+ tail += 2;
+ break;
+
+ default:
+ /*
+ * If the op is not a special byte code,
+ * it is a ctrl char tagged to be echoed
+ * as "^X" (where X is the letter
+ * representing the control char).
+ * Note that we must ensure there is
+ * enough space for the whole ctrl pair.
+ *
+ */
+ ch = '^';
+ rt_device_write((rt_device_t)tty, -1, &ch, 1);
+ ch = op ^ 0100;
+ rt_device_write((rt_device_t)tty, -1, &ch, 1);
+ ldata->column += 2;
+ tail += 2;
+ }
+ }
+ else
+ {
+ if (O_OPOST(tty))
+ {
+ int retval = do_output_char(c, tty);
+ if (retval < 0)
+ {
+ break;
+ }
+ }
+ else
+ {
+ rt_device_write((rt_device_t)tty, -1, &c, 1);
+ }
+ tail += 1;
+ }
+ }
+
+ /* If the echo buffer is nearly full (so that the possibility exists
+ * of echo overrun before the next commit), then discard enough
+ * data at the tail to prevent a subsequent overrun */
+ while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK)
+ {
+ if (echo_buf(ldata, tail) == ECHO_OP_START)
+ {
+ if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB)
+ {
+ tail += 3;
+ }
+ else
+ {
+ tail += 2;
+ }
+ }
+ else
+ {
+ tail++;
+ }
+ }
+
+ ldata->echo_tail = tail;
+ return 0;
+}
+
+static void commit_echoes(struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ size_t nr = 0, old = 0;
+ size_t head = 0;
+
+ head = ldata->echo_head;
+ ldata->echo_mark = head;
+ old = ldata->echo_commit - ldata->echo_tail;
+
+ /* Process committed echoes if the accumulated # of bytes
+ * is over the threshold (and try again each time another
+ * block is accumulated) */
+ nr = head - ldata->echo_tail;
+ if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK))
+ {
+ return;
+ }
+
+ ldata->echo_commit = head;
+ __process_echoes(tty);
+}
+
+static void process_echoes(struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ if (ldata->echo_mark == ldata->echo_tail)
+ {
+ return;
+ }
+
+ ldata->echo_commit = ldata->echo_mark;
+ __process_echoes(tty);
+}
+
+/* NB: echo_mark and echo_head should be equivalent here */
+static void flush_echoes(struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ if ((!L_ECHO(tty) && !L_ECHONL(tty)) ||
+ ldata->echo_commit == ldata->echo_head)
+ {
+ return;
+ }
+
+ ldata->echo_commit = ldata->echo_head;
+ __process_echoes(tty);
+}
+/**
+ * n_tty_set_termios - termios data changed
+ * @tty: terminal
+ * @old: previous data
+ *
+ * Called by the tty layer when the user changes termios flags so
+ * that the line discipline can plan ahead. This function cannot sleep
+ * and is protected from re-entry by the tty layer. The user is
+ * guaranteed that this function will not be re-entered or in progress
+ * when the ldisc is closed.
+ *
+ * Locking: Caller holds tty->termios_rwsem
+ */
+
+static void n_tty_set_termios(struct tty_struct *tty, struct termios *old)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ if (!old || (old->c_lflag ^ tty->init_termios.c_lflag) & (ICANON | EXTPROC))
+ {
+ rt_memset(ldata->read_flags, 0, RT_TTY_BUF);
+ ldata->line_start = ldata->read_tail;
+ if (!L_ICANON(tty) || !read_cnt(ldata))
+ {
+ ldata->canon_head = ldata->read_tail;
+ ldata->push = 0;
+ }
+ else
+ {
+ set_bit((ldata->read_head - 1) & (RT_TTY_BUF - 1),(int *)ldata->read_flags);
+ ldata->canon_head = ldata->read_head;
+ ldata->push = 1;
+ }
+ ldata->commit_head = ldata->read_head;
+ ldata->erasing = 0;
+ ldata->lnext = 0;
+ }
+
+ ldata->icanon = (L_ICANON(tty) != 0);
+
+ if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) ||
+ I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
+ I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
+ I_PARMRK(tty))
+ {
+ rt_memset(ldata->char_map, 0, 256);
+ if (I_IGNCR(tty) || I_ICRNL(tty))
+ {
+ set_bit('\r', (int *)ldata->char_map);
+ }
+
+ if (I_INLCR(tty))
+ {
+ set_bit('\n', (int *)ldata->char_map);
+ }
+
+ if (L_ICANON(tty))
+ {
+ set_bit(ERASE_CHAR(tty), (int *)ldata->char_map);
+ set_bit(KILL_CHAR(tty),(int *) ldata->char_map);
+ set_bit(EOF_CHAR(tty), (int *)ldata->char_map);
+ set_bit('\n',(int *) ldata->char_map);
+ set_bit(EOL_CHAR(tty),(int *) ldata->char_map);
+ if (L_IEXTEN(tty))
+ {
+ set_bit(WERASE_CHAR(tty), (int *)ldata->char_map);
+ set_bit(LNEXT_CHAR(tty), (int *)ldata->char_map);
+ set_bit(EOL2_CHAR(tty), (int *)ldata->char_map);
+ if (L_ECHO(tty))
+ {
+ set_bit(REPRINT_CHAR(tty),
+ (int *)ldata->char_map);
+ }
+ }
+ }
+ if (I_IXON(tty))
+ {
+ set_bit(START_CHAR(tty), (int *)ldata->char_map);
+ set_bit(STOP_CHAR(tty), (int *)ldata->char_map);
+ }
+ if (L_ISIG(tty))
+ {
+ set_bit(INTR_CHAR(tty), (int *)ldata->char_map);
+ set_bit(QUIT_CHAR(tty), (int *)ldata->char_map);
+ set_bit(SUSP_CHAR(tty), (int *)ldata->char_map);
+ }
+ clear_bit(__DISABLED_CHAR, (int *)ldata->char_map);
+ ldata->raw = 0;
+ ldata->real_raw = 0;
+ }
+ else
+ {
+ ldata->raw = 1;
+ if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) &&
+ (I_IGNPAR(tty) || !I_INPCK(tty))/* &&
+ (tty->driver->flags & TTY_DRIVER_REAL_RAW)*/)
+ {
+ ldata->real_raw = 1;
+ }
+ else
+ {
+ ldata->real_raw = 0;
+ }
+ }
+}
+
+void console_ldata_init(struct tty_struct *tty)
+{
+ struct n_tty_data *ldata = RT_NULL;
+
+ ldata = rt_malloc(sizeof(struct n_tty_data));
+ if (ldata == RT_NULL)
+ {
+ LOG_E("console_ldata_init ldata malloc fail");
+ return;
+ }
+ tty->disc_data = ldata;
+ reset_buffer_flags(ldata);
+ ldata->column = 0;
+ ldata->canon_column = 0;
+ ldata->no_room = 0;
+ ldata->lnext = 0;
+ n_tty_set_termios(tty, RT_NULL);
+ return;
+}
+
+static int n_tty_open(struct dfs_fd *fd)
+{
+ int ret = 0;
+ struct n_tty_data *ldata = RT_NULL;
+ struct tty_struct *tty = (struct tty_struct*)fd->vnode->data;
+
+ ldata = rt_malloc(sizeof(struct n_tty_data));
+ if (ldata == RT_NULL)
+ {
+ LOG_E("n_tty_open ldata malloc fail");
+ return -1;
+ }
+
+ ldata->atomic_read_lock = rt_mutex_create("atomic_read_lock",RT_IPC_FLAG_FIFO);
+ ldata->output_lock = rt_mutex_create("output_lock",RT_IPC_FLAG_FIFO);
+ tty->disc_data = ldata;
+ reset_buffer_flags(ldata);
+ ldata->column = 0;
+ ldata->canon_column = 0;
+ ldata->lnext = 0;
+ n_tty_set_termios(tty, RT_NULL);
+ return ret;
+}
+
+static inline int input_available_p(struct tty_struct *tty, int poll)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ int amt = poll && !TIME_CHAR(tty) && MIN_CHAR(tty) ? MIN_CHAR(tty) : 1;
+
+ if (ldata->icanon && !L_EXTPROC(tty))
+ {
+ return ldata->canon_head != ldata->read_tail;
+ }
+ else
+ {
+ return ldata->commit_head - ldata->read_tail >= amt;
+ }
+}
+
+/**
+ * copy_from_read_buf - copy read data directly
+ * @tty: terminal device
+ * @b: user data
+ * @nr: size of data
+ *
+ * Helper function to speed up n_tty_read. It is only called when
+ * ICANON is off; it copies characters straight from the tty queue to
+ * user space directly. It can be profitably called twice; once to
+ * drain the space from the tail pointer to the (physical) end of the
+ * buffer, and once to drain the space from the (physical) beginning of
+ * the buffer to head pointer.
+ *
+ * Called under the ldata->atomic_read_lock sem
+ *
+ * n_tty_read()/consumer path:
+ * caller holds non-exclusive termios_rwsem
+ * read_tail published
+ */
+
+static int copy_from_read_buf(struct tty_struct *tty,char *b,size_t nr)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ size_t n = 0;
+ rt_bool_t is_eof = 0;
+ size_t head = ldata->commit_head;
+ size_t tail = ldata->read_tail & (RT_TTY_BUF - 1);
+
+ n = min(head - ldata->read_tail, RT_TTY_BUF - tail);
+ n = min(nr, n);
+ if (n)
+ {
+ const char *from = read_buf_addr(ldata, tail);
+ rt_memcpy(b, from, n);
+ is_eof = n == 1 && *from == EOF_CHAR(tty);
+ ldata->read_tail += n;
+ /* Turn single EOF into zero-length read */
+ if (L_EXTPROC(tty) && ldata->icanon && is_eof &&
+ (head == ldata->read_tail))
+ {
+ n = 0;
+ }
+
+ }
+ return n;
+}
+
+/**
+ * canon_copy_from_read_buf - copy read data in canonical mode
+ * @tty: terminal device
+ * @b: user data
+ * @nr: size of data
+ *
+ * Helper function for n_tty_read. It is only called when ICANON is on;
+ * it copies one line of input up to and including the line-delimiting
+ * character into the user-space buffer.
+ *
+ * NB: When termios is changed from non-canonical to canonical mode and
+ * the read buffer contains data, n_tty_set_termios() simulates an EOF
+ * push (as if C-d were input) _without_ the DISABLED_CHAR in the buffer.
+ * This causes data already processed as input to be immediately available
+ * as input although a newline has not been received.
+ *
+ * Called under the atomic_read_lock mutex
+ *
+ * n_tty_read()/consumer path:
+ * caller holds non-exclusive termios_rwsem
+ * read_tail published
+ */
+
+static int canon_copy_from_read_buf(struct tty_struct *tty, char *b, size_t nr)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ size_t n = 0, size = 0, more = 0, c = 0;
+ size_t eol = 0;
+ size_t tail = 0;
+ int found = 0;
+
+ /* N.B. avoid overrun if nr == 0 */
+ if (nr == 0)
+ {
+ return 0;
+ }
+
+ n = min(nr + 1, ldata->canon_head - ldata->read_tail);
+
+ tail = ldata->read_tail & (RT_TTY_BUF - 1);
+ size = min(tail + n, RT_TTY_BUF);
+
+ eol = find_next_bit(ldata->read_flags, size, tail);
+ more = n - (size - tail);
+ if (eol == RT_TTY_BUF && more)
+ {
+ /* scan wrapped without finding set bit */
+ eol = find_next_bit(ldata->read_flags, more, 0);
+ found = eol != more;
+ }
+ else
+ {
+ found = eol != size;
+ }
+
+ n = eol - tail;
+ if (n > RT_TTY_BUF)
+ {
+ n += RT_TTY_BUF;
+ }
+ c = n + found;
+
+ if (!found || read_buf(ldata, eol) != __DISABLED_CHAR)
+ {
+ c = min(nr, c);
+ n = c;
+ }
+
+ size_t buf_size = RT_TTY_BUF - tail;
+ const void *from = read_buf_addr(ldata, tail);
+ if (n > buf_size)
+ {
+ rt_memcpy(b, from, buf_size);
+ b += buf_size;
+ n -= buf_size;
+ from = ldata->read_buf;
+ }
+ rt_memcpy(b, from, n);
+
+ if (found)
+ {
+ clear_bit(eol, (int *)ldata->read_flags);
+ }
+ ldata->read_tail = ldata->read_tail + c;
+
+ if (found)
+ {
+ if (!ldata->push)
+ {
+ ldata->line_start = ldata->read_tail;
+ }
+ else
+ {
+ ldata->push = 0;
+ }
+ }
+ return n;
+}
+
+
+static int n_tty_close(struct tty_struct *tty)
+{
+ int ret = 0;
+ struct n_tty_data *ldata = RT_NULL;
+ struct tty_struct *o_tty = RT_NULL;
+
+ RT_ASSERT(tty != RT_NULL);
+ if (tty->type == TTY_DRIVER_TYPE_PTY && tty->subtype == PTY_TYPE_MASTER)
+ {
+ o_tty = tty->other_struct;
+ }
+ else
+ {
+ o_tty = tty;
+ }
+
+ ldata = o_tty->disc_data;
+ rt_free(ldata);
+ o_tty->disc_data = RT_NULL;
+ return ret;
+}
+
+static int n_tty_ioctl(struct dfs_fd *fd, int cmd, void *args)
+{
+ int ret = 0;
+ struct tty_struct *real_tty = RT_NULL;
+ struct tty_struct *tty = RT_NULL;
+
+ tty = (struct tty_struct *)fd->vnode->data;
+ RT_ASSERT(tty != RT_NULL);
+ if (tty->type == TTY_DRIVER_TYPE_PTY && tty->subtype == PTY_TYPE_MASTER)
+ {
+ real_tty = tty->other_struct;
+ }
+ else
+ {
+ real_tty = tty;
+ }
+
+ switch(cmd)
+ {
+
+ default:
+ ret = n_tty_ioctl_extend(real_tty, cmd, args);
+ if (ret != -ENOIOCTLCMD)
+ {
+ return ret;
+ }
+ }
+
+ ret = rt_device_control((rt_device_t)real_tty, cmd, args);
+ if (ret != -ENOIOCTLCMD)
+ {
+ return ret;
+ }
+ return ret;
+}
+
+static void
+n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c)
+{
+ isig(signal, tty);
+ if (L_ECHO(tty))
+ {
+ echo_char(c, tty);
+ commit_echoes(tty);
+ }
+ else
+ {
+ process_echoes(tty);
+ }
+ return;
+}
+
+/**
+ * n_tty_receive_char - perform processing
+ * @tty: terminal device
+ * @c: character
+ *
+ * Process an individual character of input received from the driver.
+ * This is serialized with respect to itself by the rules for the
+ * driver above.
+ *
+ * n_tty_receive_buf()/producer path:
+ * caller holds non-exclusive termios_rwsem
+ * publishes canon_head if canonical mode is active
+ *
+ * Returns 1 if LNEXT was received, else returns 0
+ */
+
+static int n_tty_receive_char_special(struct tty_struct *tty, unsigned char c)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ if (I_IXON(tty))
+ {
+ if (c == START_CHAR(tty)) /*ctrl + p realize missing*/
+ {
+ process_echoes(tty);
+ return 0;
+ }
+ if (c == STOP_CHAR(tty)) /*ctrl + s realize missing*/
+ {
+ return 0;
+ }
+ }
+
+ if (L_ISIG(tty))
+ {
+ if (c == INTR_CHAR(tty)) /*ctrl + c realize missing*/
+ {
+ n_tty_receive_signal_char(tty, SIGINT, c);
+ return 0;
+ }
+ else if (c == QUIT_CHAR(tty)) /*ctrl + d realize missing*/
+ {
+ n_tty_receive_signal_char(tty, SIGQUIT, c);
+ return 0;
+ }
+ else if (c == SUSP_CHAR(tty)) /*ctrl + z realize missing*/
+ {
+ n_tty_receive_signal_char(tty, SIGTSTP, c);
+ return 0;
+ }
+ }
+
+ if (c == '\r')
+ {
+ if (I_IGNCR(tty))
+ {
+ return 0;
+ }
+
+ if (I_ICRNL(tty))
+ {
+ c = '\n';
+ }
+ }
+ else if (c == '\n' && I_INLCR(tty))
+ {
+ c = '\r';
+ }
+
+ if (ldata->icanon)
+ {
+ if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
+ (c == WERASE_CHAR(tty) && L_IEXTEN(tty)))
+ {
+ eraser(c, tty);
+ commit_echoes(tty);
+ return 0;
+ }
+ if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty))
+ {
+ ldata->lnext = 1;
+ if (L_ECHO(tty))
+ {
+ finish_erasing(ldata);
+ if (L_ECHOCTL(tty))
+ {
+ echo_char_raw('^', ldata);
+ echo_char_raw('\b', ldata);
+ commit_echoes(tty);
+ }
+ }
+ return 1;
+ }
+ if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty))
+ {
+ size_t tail = ldata->canon_head;
+
+ finish_erasing(ldata);
+ echo_char(c, tty);
+ echo_char_raw('\n', ldata);
+ while (tail != ldata->read_head)
+ {
+ echo_char(read_buf(ldata, tail), tty);
+ tail++;
+ }
+ commit_echoes(tty);
+ return 0;
+ }
+ if (c == '\n')
+ {
+ if (L_ECHO(tty) || L_ECHONL(tty))
+ {
+ echo_char_raw('\n', ldata);
+ commit_echoes(tty);
+ }
+ goto handle_newline;
+ }
+ if (c == EOF_CHAR(tty))
+ {
+ c = __DISABLED_CHAR;
+ goto handle_newline;
+ }
+ if ((c == EOL_CHAR(tty)) ||
+ (c == EOL2_CHAR(tty) && L_IEXTEN(tty)))
+ {
+ /*
+ * XXX are EOL_CHAR and EOL2_CHAR echoed?!?
+ */
+ if (L_ECHO(tty))
+ {
+ /* Record the column of first canon char. */
+ if (ldata->canon_head == ldata->read_head)
+ {
+ echo_set_canon_col(ldata);
+ }
+
+ echo_char(c, tty);
+ commit_echoes(tty);
+ }
+ /*
+ * XXX does PARMRK doubling happen for
+ * EOL_CHAR and EOL2_CHAR?
+ */
+ if (c == (unsigned char) '\377' && I_PARMRK(tty))
+ {
+ put_tty_queue(c, ldata);
+ }
+
+handle_newline:
+ set_bit(ldata->read_head & (RT_TTY_BUF - 1), (int *)ldata->read_flags);
+ put_tty_queue(c, ldata);
+ ldata->canon_head = ldata->read_head;
+ tty_wakeup_check(tty);
+ return 0;
+ }
+ }
+
+ if (L_ECHO(tty))
+ {
+ finish_erasing(ldata);
+ if (c == '\n')
+ {
+ echo_char_raw('\n', ldata);
+ }
+ else
+ {
+ /* Record the column of first canon char. */
+ if (ldata->canon_head == ldata->read_head)
+ {
+ echo_set_canon_col(ldata);
+ }
+ echo_char(c, tty);
+ }
+ commit_echoes(tty);
+ }
+
+ /* PARMRK doubling check */
+ if (c == (unsigned char) '\377' && I_PARMRK(tty))
+ {
+ put_tty_queue(c, ldata);
+ }
+
+ put_tty_queue(c, ldata);
+ return 0;
+}
+
+static inline void n_tty_receive_char_inline(struct tty_struct *tty, unsigned char c)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ if (L_ECHO(tty))
+ {
+ finish_erasing(ldata);
+ /* Record the column of first canon char. */
+ if (ldata->canon_head == ldata->read_head)
+ {
+ echo_set_canon_col(ldata);
+ }
+
+ echo_char(c, tty);
+ commit_echoes(tty);
+ }
+ /* PARMRK doubling check */
+ if (c == (unsigned char) '\377' && I_PARMRK(tty))
+ {
+ put_tty_queue(c, ldata);
+ }
+
+ put_tty_queue(c, ldata);
+}
+
+static void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
+{
+ n_tty_receive_char_inline(tty, c);
+}
+
+static void n_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ ldata->lnext = 0;
+ if (flag == TTY_NORMAL)
+ {
+ if (I_ISTRIP(tty))
+ {
+ c &= 0x7f;
+ }
+
+ if (I_IUCLC(tty) && L_IEXTEN(tty))
+ {
+ c = tolower(c);
+ }
+
+ n_tty_receive_char(tty, c);
+ }
+ else
+ {
+ //n_tty_receive_char_flagged(tty, c, flag);
+ }
+
+}
+
+static void n_tty_receive_buf_real_raw(struct tty_struct *tty, char *cp, int count)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ size_t n = 0, head = 0;
+
+ head = ldata->read_head & (RT_TTY_BUF - 1);
+ n = min(count, RT_TTY_BUF - head);
+ rt_memcpy(read_buf_addr(ldata, head), cp, n);
+ ldata->read_head += n;
+ cp += n;
+ count -= n;
+
+ head = ldata->read_head & (RT_TTY_BUF - 1);
+ n = min(count, RT_TTY_BUF - head);
+ rt_memcpy(read_buf_addr(ldata, head), cp, n);
+ ldata->read_head += n;
+}
+
+static void n_tty_receive_buf_raw(struct tty_struct *tty, char *cp, int count)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ char flag = TTY_NORMAL;
+
+ while (count--)
+ {
+ if (flag == TTY_NORMAL)
+ {
+ put_tty_queue(*cp++, ldata);
+ }
+ }
+}
+
+static void n_tty_receive_buf_standard(struct tty_struct *tty, char *cp, int count)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ char flag = TTY_NORMAL;
+
+ while (count--)
+ {
+ char c = *cp++;
+
+ if (I_ISTRIP(tty))
+ {
+ c &= 0x7f;
+ }
+
+ if (I_IUCLC(tty) && L_IEXTEN(tty))
+ {
+ c = tolower(c);
+ }
+
+ if (L_EXTPROC(tty))
+ {
+ put_tty_queue(c, ldata);
+ continue;
+ }
+
+ if (!test_bit(c, (int *)ldata->char_map))
+ {
+ n_tty_receive_char_inline(tty, c);
+ }
+ else if (n_tty_receive_char_special(tty, c) && count)
+ {
+ n_tty_receive_char_lnext(tty, *cp++, flag);
+ count--;
+ }
+ }
+}
+
+static inline void n_tty_receive_char_fast(struct tty_struct *tty, unsigned char c)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+
+ if (L_ECHO(tty))
+ {
+ finish_erasing(ldata);
+ /* Record the column of first canon char. */
+ if (ldata->canon_head == ldata->read_head)
+ {
+ echo_set_canon_col(ldata);
+ }
+
+ echo_char(c, tty);
+ commit_echoes(tty);
+ }
+ put_tty_queue(c, ldata);
+}
+
+static void n_tty_receive_buf_fast(struct tty_struct *tty, char *cp, int count)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ char flag = TTY_NORMAL;
+
+ while (count--)
+ {
+ unsigned char c = *cp++;
+
+ if (!test_bit(c, (int *)ldata->char_map))
+ {
+ n_tty_receive_char_fast(tty, c);
+ }
+ else if (n_tty_receive_char_special(tty, c) && count)
+ {
+ n_tty_receive_char_lnext(tty, *cp++, flag);
+ count--;
+ }
+ }
+}
+
+static void __receive_buf(struct tty_struct *tty, char *cp, int count)
+{
+ struct n_tty_data *ldata = tty->disc_data;
+ rt_bool_t preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty));
+
+ if (ldata->real_raw)
+ {
+ n_tty_receive_buf_real_raw(tty, cp, count);
+ }
+
+ else if (ldata->raw || (L_EXTPROC(tty) && !preops))
+ {
+ n_tty_receive_buf_raw(tty, cp, count);
+ }
+ else
+ {
+ if (!preops && !I_PARMRK(tty))
+ {
+ n_tty_receive_buf_fast(tty, cp, count);
+ }
+ else
+ {
+ n_tty_receive_buf_standard(tty, cp, count);
+ }
+ flush_echoes(tty);
+ }
+
+ if (ldata->icanon && !L_EXTPROC(tty))
+ return;
+
+ /* publish read_head to consumer */
+ ldata->commit_head = ldata->read_head;
+
+ if (read_cnt(ldata))
+ {
+ tty_wakeup_check(tty);
+ }
+}
+
+int n_tty_receive_buf(struct tty_struct *tty,char *cp, int count)
+{
+ int size = 0;
+ struct n_tty_data *ldata = tty->disc_data;
+ int room = 0, n = 0, rcvd = 0, overflow = 0;
+
+ size = count;
+ while(1)
+ {
+ size_t tail = ldata->read_tail;
+
+ room = RT_TTY_BUF - (ldata->read_head - tail);
+
+ if (I_PARMRK(tty))
+ {
+ room = (room +2)/3;
+ }
+ room--;
+ if (room <= 0)
+ {
+ overflow = ldata->icanon && ldata->canon_head == tail;
+ if (overflow && room < 0)
+ {
+ ldata->read_head--;
+ }
+
+ room = overflow;
+ }
+ else
+ {
+ overflow = 0;
+ }
+
+ n = min(size, room);
+
+ if (!n)
+ {
+ break;
+ }
+
+ if (!overflow)
+ {
+ __receive_buf(tty, cp, n);
+ }
+
+ cp += n;
+ size -= n;
+ rcvd += n;
+ }
+ return count - size;
+}
+
+/**
+ * job_control - check job control
+ * @tty: tty
+ * @file: file handle
+ *
+ * Perform job control management checks on this file/tty descriptor
+ * and if appropriate send any needed signals and return a negative
+ * error code if action should be taken.
+ *
+ * Locking: redirected write test is safe
+ * current->signal->tty check is safe
+ * ctrl_lock to safely reference tty->pgrp
+ */
+
+static int job_control(struct tty_struct *tty)
+{
+ return __tty_check_change(tty, SIGTTIN);
+}
+
+static int n_tty_read(struct dfs_fd *fd, void *buf, size_t count)
+{
+ int level = 0;
+ char *b = (char *)buf;
+ struct tty_struct *tty = RT_NULL;
+ struct rt_lwp *lwp = RT_NULL;
+ struct rt_wqueue *wq = RT_NULL;
+ int wait_ret = 0;
+ int retval = 0;
+ int c = 0;
+
+ level = rt_hw_interrupt_disable();
+ tty = (struct tty_struct *)fd->vnode->data;
+ RT_ASSERT(tty != RT_NULL);
+ c = job_control(tty);
+ if (c < 0)
+ {
+ rt_hw_interrupt_enable(level);
+ return c;
+ }
+
+ struct n_tty_data *ldata = tty->disc_data;
+
+ lwp = (struct rt_lwp *)(rt_thread_self()->lwp);
+ wq = wait_queue_get(lwp, tty);
+
+ while(count)
+ {
+ if ((!input_available_p(tty, 0)))
+ {
+ if (fd->flags & O_NONBLOCK)
+ {
+ retval = -EAGAIN;
+ break;
+ }
+
+ wait_ret = rt_wqueue_wait_interruptible(wq, 0, RT_WAITING_FOREVER);
+ if (wait_ret != 0)
+ {
+ break;
+ }
+ }
+
+ if (ldata->icanon && !L_EXTPROC(tty))
+ {
+ retval = canon_copy_from_read_buf(tty, b, count);
+ }
+ else
+ {
+ retval = copy_from_read_buf(tty, b, count);
+ }
+
+ if (retval >= 1)
+ {
+ break;
+ }
+ }
+ rt_hw_interrupt_enable(level);
+ return retval;
+}
+
+static int n_tty_write(struct dfs_fd *fd, const void *buf, size_t count)
+{
+ int retval = 0;
+ char *b = (char *)buf;
+ int c = 0;
+ struct tty_struct *tty = RT_NULL;
+
+ tty = (struct tty_struct *)fd->vnode->data;
+ RT_ASSERT(tty != RT_NULL);
+ retval = tty_check_change(tty);
+ if (retval)
+ {
+ return retval;
+ }
+
+ process_echoes(tty);
+ retval = count;
+ while(1)
+ {
+ if (O_OPOST(tty))
+ {
+ while (count > 0)
+ {
+ ssize_t num = process_output_block(tty, b, count);
+ if (num < 0)
+ {
+ if (num == -EAGAIN)
+ {
+ break;
+ }
+
+ retval = num;
+ goto break_out;
+ }
+ b += num;
+ count -= num;
+ if (count == 0)
+ {
+ break;
+ }
+
+ c = *b;
+ if (process_output(c, tty) < 0)
+ {
+ break;
+ }
+
+ b++;
+ count--;
+ }
+ retval -= count;
+ }
+ else
+ {
+ while (count > 0)
+ {
+ c = rt_device_write((rt_device_t)tty, -1, b, count);
+ if (c < 0)
+ {
+ retval = c;
+ goto break_out;
+ }
+ b += c;
+ count -= c;
+ }
+ retval -= count;
+ }
+
+ if (!count)
+ {
+ break;
+ }
+ if (fd->flags & O_NONBLOCK)
+ {
+ break;
+ }
+ }
+break_out:
+ return retval;
+}
+
+static int n_tty_flush(struct dfs_fd *fd)
+{
+ return 0;
+}
+
+static int n_tty_lseek(struct dfs_fd *fd, off_t offset)
+{
+ return 0;
+}
+
+static int n_tty_getdents(struct dfs_fd *fd, struct dirent *dirp, uint32_t count)
+{
+ return 0;
+}
+
+static int n_tty_poll(struct dfs_fd *fd, struct rt_pollreq *req)
+{
+ rt_base_t level = 0;
+ int mask = POLLOUT;
+ struct tty_struct *tty = RT_NULL;
+ struct rt_wqueue *wq = RT_NULL;
+ struct rt_lwp *lwp = RT_NULL;
+
+ tty = (struct tty_struct *)fd->vnode->data;
+ RT_ASSERT(tty != RT_NULL);
+
+ RT_ASSERT(tty->init_flag == TTY_INIT_FLAG_INITED);
+
+ lwp = (struct rt_lwp *)(rt_thread_self()->lwp);
+ wq = wait_queue_get(lwp, tty);
+ rt_poll_add(wq, req);
+ level = rt_hw_interrupt_disable();
+
+ if (input_available_p(tty, 1))
+ {
+ mask |= POLLIN;
+ }
+
+ rt_hw_interrupt_enable(level);
+
+ return mask;
+}
+
+static struct tty_ldisc_ops n_tty_ops = {
+ "n_tty",
+ 0,
+ n_tty_open,
+ n_tty_close,
+ n_tty_ioctl,
+ n_tty_read,
+ n_tty_write,
+ n_tty_flush,
+ n_tty_lseek,
+ n_tty_getdents,
+ n_tty_poll,
+ n_tty_set_termios,
+ n_tty_receive_buf,
+ 0,
+};
+
+void n_tty_init(void)
+{
+ tty_register_ldisc(N_TTY, &n_tty_ops);
+}
diff --git a/components/drivers/tty/pty.c b/components/drivers/tty/pty.c
new file mode 100644
index 0000000000..b3e6e7519a
--- /dev/null
+++ b/components/drivers/tty/pty.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021.12.07 linzhenxing first version
+ */
+#include <rtthread.h>
+#include <tty.h>
+#include <tty_ldisc.h>
+
+#define DBG_TAG "PTY"
+#ifdef RT_TTY_DEBUG
+#define DBG_LVL DBG_LOG
+#else
+#define DBG_LVL DBG_INFO
+#endif /* RT_TTY_DEBUG */
+#include <rtdbg.h>
+
+#define PTY_PTS_SIZE 10
+static struct tty_struct ptm_driver;
+static struct tty_struct pts_drivers[PTY_PTS_SIZE];
+static int pts_index = 0;
+
+static int pts_register(struct tty_struct *ptm_drv, struct tty_struct *pts_drv, int pts_index);
+
+/* check free pts device */
+static struct tty_struct *find_freepts(void)
+{
+ for(int i = 0; i < PTY_PTS_SIZE; i++)
+ {
+ if (pts_drivers[i].init_flag == TTY_INIT_FLAG_NONE)
+ {
+ pts_drivers[i].init_flag = TTY_INIT_FLAG_ALLOCED;
+ return &pts_drivers[i];
+ }
+ }
+ return RT_NULL;
+}
+
+/* Set the lock flag on a pty */
+static int pty_set_lock(struct tty_struct *tty, int *arg)
+{
+ int val = *arg;
+
+ if (val)
+ {
+ tty->pts_lock = val;
+ }
+ else
+ {
+ tty->pts_lock = val;
+ }
+ return 0;
+}
+
+static int pty_get_lock(struct tty_struct *tty, int *arg)
+{
+ *arg = tty->pts_lock;
+ return 0;
+}
+
+static int pty_get_index(struct tty_struct *tty, int *arg)
+{
+ *arg = tty->index;
+ return 0;
+}
+/* RT-Thread Device Interface */
+/*
+ * This function initializes console device.
+ */
+static rt_err_t pty_device_init(struct rt_device *dev)
+{
+ rt_err_t result = RT_EOK;
+ int level = 0;
+ struct tty_struct *tty = RT_NULL;
+ RT_ASSERT(dev != RT_NULL);
+
+ tty = (struct tty_struct *)dev;
+ level = rt_hw_interrupt_disable();
+ RT_ASSERT(tty->init_flag == TTY_INIT_FLAG_REGED);
+ tty->init_flag = TTY_INIT_FLAG_INITED;
+ rt_hw_interrupt_enable(level);
+ return result;
+}
+
+static rt_err_t pty_device_open(struct rt_device *dev, rt_uint16_t oflag)
+{
+ rt_err_t result = RT_EOK;
+ return result;
+}
+
+static rt_err_t pty_device_close(struct rt_device *dev)
+{
+ rt_err_t result = RT_EOK;
+ struct tty_struct *tty = (struct tty_struct*)dev;
+ //struct tty_struct *to = RT_NULL;
+
+ if (tty->subtype == PTY_TYPE_MASTER)
+ {
+ // to = tty->other_struct;
+ // to->init_flag = TTY_INIT_FLAG_NONE;
+ // to->other_struct = RT_NULL;
+ // to->foreground = RT_NULL;
+ // to->index = -1;
+ // tty_ldisc_kill(to);
+ // tty->other_struct = RT_NULL;
+ }
+ else
+ {
+ // to = tty->other_struct;
+ // to->other_struct = RT_NULL;
+ // tty->init_flag = TTY_INIT_FLAG_NONE;
+ // tty->other_struct = RT_NULL;
+ // tty->foreground = RT_NULL;
+ // tty->index = -1;
+ // tty->other_struct = RT_NULL;
+ // tty_ldisc_kill(tty);
+ }
+
+ return result;
+}
+
+static rt_size_t pty_device_read(struct rt_device *dev,
+ rt_off_t pos,
+ void *buffer,
+ rt_size_t size)
+{
+ rt_size_t len = 0;
+
+ return len;
+}
+
+static rt_size_t pty_device_write(struct rt_device *dev,
+ rt_off_t pos,
+ const void *buffer,
+ rt_size_t size)
+{
+ rt_base_t level = 0;
+ rt_size_t len = 0;
+ struct tty_struct *tty = RT_NULL;
+ struct tty_struct *to = RT_NULL;
+
+ tty = (struct tty_struct *)dev;
+ RT_ASSERT(tty != RT_NULL);
+ RT_ASSERT(tty->init_flag == TTY_INIT_FLAG_INITED);
+ to = tty->other_struct;
+ level = rt_hw_interrupt_disable();
+
+ if (to->ldisc->ops->receive_buf)
+ {
+ len = to->ldisc->ops->receive_buf(to, (char *)buffer, size);
+ }
+ rt_hw_interrupt_enable(level);
+
+ return len;
+}
+
+static rt_err_t pty_device_control(rt_device_t dev, int cmd, void *args)
+{
+ struct tty_struct *tty = (struct tty_struct *)dev;
+
+ switch (cmd)
+ {
+ case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
+ return pty_set_lock(tty, (int *)args);
+ case TIOCGPTLCK: /* Get PT Lock status */
+ return pty_get_lock(tty, (int *)args);
+ case TIOCGPTN: /* Get PT Number */
+ return pty_get_index(tty, (int *)args);
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static int ptmx_open(struct dfs_fd *fd)
+{
+ int ret = 0;
+ struct tty_struct *tty = RT_NULL;
+ struct tty_struct *pts_drv = RT_NULL;
+ struct tty_ldisc *ld = RT_NULL;
+ struct rt_lwp *lwp = RT_NULL;
+ struct rt_wqueue *wq = RT_NULL;
+
+ tty = (struct tty_struct *)fd->vnode->data;
+ RT_ASSERT(tty != RT_NULL);
+
+ pts_drv = find_freepts();
+ if (pts_drv == RT_NULL)
+ {
+ LOG_E("free pts driver find fail\n");
+ return -1;
+ }
+ ret = pts_register(tty, pts_drv, pts_index);
+ if (ret < 0)
+ {
+ LOG_E("pts register fail\n");
+ rt_free(pts_drv);
+ return -1;
+ }
+ pts_index++;
+ lwp = (struct rt_lwp *)(rt_thread_self()->lwp);
+ wq = wait_queue_get(lwp, tty);
+ pts_drv->wait_queue = *wq;
+ tty->other_struct = pts_drv;
+ ld = tty->ldisc;
+ if (ld->ops->open)
+ {
+ ret = ld->ops->open(fd);
+ }
+
+ return ret;
+}
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops pty_device_ops =
+{
+ pty_device_init,
+ pty_device_open,
+ pty_device_close,
+ pty_device_read,
+ pty_device_write,
+ pty_device_control,
+};
+#endif /* RT_USING_DEVICE_OPS */
+static struct dfs_file_ops pts_fops;
+static struct dfs_file_ops ptmx_fops;
+static int pts_register(struct tty_struct *ptm_drv, struct tty_struct *pts_drv, int pts_index)
+{
+ rt_err_t ret = RT_EOK;
+ rt_base_t level = 0;
+ struct rt_device *device = RT_NULL;
+ char name[20];
+
+ RT_ASSERT(ptm_drv!=RT_NULL);
+ level = rt_hw_interrupt_disable();
+
+ if (pts_drv->init_flag != TTY_INIT_FLAG_ALLOCED)
+ {
+ LOG_E("pts%d has been registered\n", pts_index);
+ ret = (-RT_EBUSY);
+ goto _exit;
+ }
+
+ device = &pts_drv->parent;
+ device->type = RT_Device_Class_Char;
+#ifdef RT_USING_DEVICE_OPS
+ device->ops = &pty_device_ops;
+#else
+ device->init = pty_device_init;
+ device->open = pty_device_open;
+ device->close = pty_device_close;
+ device->read = pty_device_read;
+ device->write = pty_device_write;
+ device->control = pty_device_control;
+#endif /* RT_USING_DEVICE_OPS */
+
+ rt_snprintf(name, sizeof(name), "pts%d", pts_index);
+ ret = rt_device_register(device, name, RT_DEVICE_FLAG_RDWR);
+ if (ret != RT_EOK)
+ {
+ LOG_E("pts%d register failed\n", pts_index);
+ ret = -RT_EIO;
+ goto _exit;
+ }
+
+#ifdef RT_USING_POSIX_DEVIO
+ /* set fops */
+ tty_set_fops(&pts_fops);
+ device->fops = &pts_fops;
+#endif
+
+ pts_drv->type = TTY_DRIVER_TYPE_PTY;
+ pts_drv->subtype = PTY_TYPE_SLAVE;
+
+ pts_drv->pgrp = -1;
+ pts_drv->session = -1;
+ pts_drv->foreground = RT_NULL;
+ pts_drv->index = pts_index;
+ pts_drv->pts_lock = 1;
+ rt_wqueue_init(&pts_drv->wait_queue);
+
+ tty_ldisc_init(pts_drv);
+
+extern struct termios tty_std_termios;
+ pts_drv->init_termios = tty_std_termios;
+ pts_drv->init_termios.c_cflag = B38400 | CS8 | CREAD;
+ pts_drv->init_termios.c_lflag |= ICANON;
+ pts_drv->init_termios.__c_ispeed = 38400;
+ pts_drv->init_termios.__c_ospeed = 38400;
+
+ pts_drv->other_struct = ptm_drv;
+
+ pts_drv->init_flag = TTY_INIT_FLAG_REGED;
+_exit:
+ rt_hw_interrupt_enable(level);
+
+ return ret;
+}
+
+static int ptmx_register(void)
+{
+ rt_base_t level = 0;
+ rt_err_t ret = RT_EOK;
+ struct rt_device *device = RT_NULL;
+ struct tty_struct *ptm_drv = &ptm_driver;
+
+ level = rt_hw_interrupt_disable();
+ RT_ASSERT(ptm_drv->init_flag == TTY_INIT_FLAG_NONE);
+
+ device = &(ptm_drv->parent);
+
+ device->type = RT_Device_Class_Char;
+
+#ifdef RT_USING_DEVICE_OPS
+ device->ops = &pty_device_ops;
+#else
+ device->init = pty_device_init;
+ device->open = pty_device_open;
+ device->close = pty_device_close;
+ device->read = pty_device_read;
+ device->write = pty_device_write;
+ device->control = pty_device_control;
+#endif /* RT_USING_DEVICE_OPS */
+
+ ret = rt_device_register(device, "ptmx", RT_DEVICE_FLAG_RDWR);
+ if (ret != RT_EOK)
+ {
+ LOG_E("ptmx register fail\n");
+ ret = -RT_EIO;
+ goto _exit;
+ }
+
+#ifdef RT_USING_POSIX_DEVIO
+ /* set fops */
+ tty_set_fops(&ptmx_fops);
+ ptmx_fops.open = ptmx_open;
+ device->fops = &ptmx_fops;
+#endif
+
+ ptm_drv->type = TTY_DRIVER_TYPE_PTY;
+ ptm_drv->subtype = PTY_TYPE_MASTER;
+ ptm_drv->head = rt_calloc(1, sizeof(struct tty_node));
+ if (!ptm_drv->head)
+ {
+ return -RT_ENOMEM;
+ }
+ tty_initstack(ptm_drv->head);
+
+ rt_spin_lock_init(&ptm_drv->spinlock);
+ ptm_drv->pgrp = -1;
+ ptm_drv->session = -1;
+ ptm_drv->foreground = RT_NULL;
+ rt_wqueue_init(&ptm_drv->wait_queue);
+
+ tty_ldisc_init(ptm_drv);
+
+extern struct termios tty_std_termios;
+ ptm_drv->init_termios.c_iflag = 0;
+ ptm_drv->init_termios.c_oflag = 0;
+ ptm_drv->init_termios.c_cflag = B38400 | CS8 | CREAD;
+ ptm_drv->init_termios.c_lflag = 0;
+ ptm_drv->init_termios.__c_ispeed = 38400;
+ ptm_drv->init_termios.__c_ospeed = 38400;
+
+ ptm_drv->init_flag = TTY_INIT_FLAG_REGED;
+
+_exit:
+ rt_hw_interrupt_enable(level);
+
+ return ret;
+}
+INIT_DEVICE_EXPORT(ptmx_register);
diff --git a/components/drivers/tty/tty.c b/components/drivers/tty/tty.c
new file mode 100644
index 0000000000..2a9380f55a
--- /dev/null
+++ b/components/drivers/tty/tty.c
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021.12.07 linzhenxing first version
+ */
+#include <dfs_file.h>
+#include <dfs_fs.h>
+#include <lwp.h>
+#include <rtdevice.h>
+#include <rtthread.h>
+#include <tty.h>
+#include <tty_ldisc.h>
+
+#if defined(RT_USING_POSIX_DEVIO)
+#include <termios.h>
+#endif
+
+#define DBG_TAG "TTY"
+#ifdef RT_TTY_DEBUG
+#define DBG_LVL DBG_LOG
+#else
+#define DBG_LVL DBG_INFO
+#endif /* RT_TTY_DEBUG */
+#include <rtdbg.h>
+
+struct termios tty_std_termios = { /* for the benefit of tty drivers */
+ .c_iflag = IMAXBEL | IUCLC | INLCR | ICRNL | IGNPAR,
+ .c_oflag = OPOST,
+ .c_cflag = B38400 | CS8 | CREAD | HUPCL,
+ .c_lflag = ISIG | ECHOE | TOSTOP | NOFLSH,
+ RT_NULL,/* .c_line = N_TTY, */
+ .c_cc = INIT_C_CC,
+ .__c_ispeed = 38400,
+ .__c_ospeed = 38400
+};
+
+void tty_initstack(struct tty_node *node)
+{
+ node->lwp = RT_NULL;
+ node->next = node;
+}
+
+static struct tty_node tty_node_cache = { RT_NULL, RT_NULL };
+
+static struct tty_node *_tty_node_alloc(void)
+{
+ struct tty_node *node = tty_node_cache.next;
+
+ if (node == RT_NULL)
+ {
+ node = rt_calloc(1, sizeof(struct tty_node));
+ }
+ else
+ {
+ tty_node_cache.next = node->next;
+ }
+
+ return node;
+}
+
+static void _tty_node_free(struct tty_node *node)
+{
+ node->next = tty_node_cache.next;
+ tty_node_cache.next = node;
+}
+
+int tty_push(struct tty_node **head, struct rt_lwp *lwp)
+{
+ struct tty_node *node = _tty_node_alloc();
+
+ if (!node)
+ {
+ return -1;
+ }
+
+ node->lwp = lwp;
+ node->next = *head;
+ *head = node;
+
+ return 0;
+}
+
+struct rt_lwp *tty_pop(struct tty_node **head, struct rt_lwp *target_lwp)
+{
+ struct tty_node *node;
+ struct rt_lwp *lwp = RT_NULL;
+
+ if (!head || !*head)
+ {
+ return RT_NULL;
+ }
+
+ node = *head;
+
+ if (target_lwp != RT_NULL && node->lwp != target_lwp)
+ {
+ struct tty_node *prev = RT_NULL;
+
+ while (node != RT_NULL && node->lwp != target_lwp)
+ {
+ prev = node;
+ node = node->next;
+ }
+
+ if (node != RT_NULL)
+ {
+ /* prev is impossible equ RT_NULL */
+ prev->next = node->next;
+ lwp = target_lwp;
+ _tty_node_free(node);
+ }
+ }
+ else
+ {
+ lwp = (*head)->lwp;
+ *head = (*head)->next;
+ node->lwp = RT_NULL;
+ _tty_node_free(node);
+ }
+
+ return lwp;
+}
+
+rt_inline int tty_sigismember(lwp_sigset_t *set, int _sig)
+{
+ unsigned long sig = _sig - 1;
+
+ if (_LWP_NSIG_WORDS == 1)
+ {
+ return 1 & (set->sig[0] >> sig);
+ }
+ else
+ {
+ return 1 & (set->sig[sig / _LWP_NSIG_BPW] >> (sig % _LWP_NSIG_BPW));
+ }
+}
+
+static int is_ignored(int sig)
+{
+ return (tty_sigismember(&current->signal_mask, sig) ||
+ current->signal_handler[sig-1] == SIG_IGN);
+}
+
+/**
+ * tty_check_change - check for POSIX terminal changes
+ * @tty: tty to check
+ *
+ * If we try to write to, or set the state of, a terminal and we're
+ * not in the foreground, send a SIGTTOU. If the signal is blocked or
+ * ignored, go ahead and perform the operation. (POSIX 7.2)
+ *
+ * Locking: ctrl_lock
+ */
+
+int __tty_check_change(struct tty_struct *tty, int sig)
+{
+ pid_t pgrp = 0, tty_pgrp = 0;
+ struct rt_lwp *lwp = tty->foreground;
+ int ret = 0;
+ int level = 0;
+
+ level = rt_hw_interrupt_disable();
+ if (current == RT_NULL)
+ {
+ rt_hw_interrupt_enable(level);
+ return 0;
+ }
+
+ if (current->tty != tty)
+ {
+ rt_hw_interrupt_enable(level);
+ return 0;
+ }
+
+ pgrp = current->__pgrp;
+ tty_pgrp = tty->pgrp;
+
+ if (tty_pgrp && (pgrp != tty->pgrp))
+ {
+ if (is_ignored(sig))
+ {
+ if (sig == SIGTTIN)
+ {
+ ret = -EIO;
+ }
+ }
+ else
+ {
+ if (lwp)
+ {
+ lwp_kill(lwp_to_pid(lwp), sig);
+ }
+ }
+ }
+ rt_hw_interrupt_enable(level);
+
+ if (!tty_pgrp)
+ {
+ LOG_D(tty, "sig=%d, tty->pgrp == -1!\n", sig);
+ }
+ return ret;
+}
+
+int tty_check_change(struct tty_struct *tty)
+{
+ return __tty_check_change(tty, SIGTTOU);
+}
+
+static int tty_open(struct dfs_fd *fd)
+{
+ int ret = 0;
+ int noctty = 0;
+ struct tty_struct *tty = RT_NULL;
+ struct tty_ldisc *ld = RT_NULL;
+
+ tty = (struct tty_struct *)fd->vnode->data;
+ RT_ASSERT(tty != RT_NULL);
+ ld = tty->ldisc;
+ if (ld->ops->open)
+ {
+ ret = ld->ops->open(fd);
+ }
+ noctty = (fd->flags & O_NOCTTY);
+
+ rt_device_t device = (rt_device_t)fd->vnode->data;
+ if (fd->vnode->ref_count == 1)
+ {
+ ret = rt_device_open(device, fd->flags);
+ }
+ if (current == RT_NULL) //kernel mode not lwp
+ {
+ return ret;
+ }
+
+ if (!noctty &&
+ current->leader &&
+ !current->tty &&
+ tty->session == -1)
+ {
+ current->tty = tty;
+ current->tty_old_pgrp = 0;
+ tty->session = current->session;
+ tty->pgrp = current->__pgrp;
+ tty->foreground = current;
+ }
+
+ return ret;
+}
+
+static int tty_close(struct dfs_fd *fd)
+{
+ int ret = 0;
+ struct tty_struct *tty = RT_NULL;
+ struct tty_ldisc *ld = RT_NULL;
+ tty = (struct tty_struct *)fd->vnode->data;
+ RT_ASSERT(tty != RT_NULL);
+ ld = tty->ldisc;
+ if (ld->ops->close)
+ {
+ //ld->ops->close(tty);
+ }
+ if (fd->vnode->ref_count == 1)
+ {
+ ret = rt_device_close((rt_device_t)tty);
+ }
+ return ret;
+}
+
+static int tiocsctty(struct tty_struct *tty, int arg)
+{
+ if (current->leader &&
+ (current->session == tty->session))
+ {
+ return 0;
+ }
+
+ /*
+ * The process must be a session leader and
+ * not have a controlling tty already.
+ */
+ if (!current->leader || current->tty)
+ {
+ return -EPERM;
+ }
+
+ if (tty->session > 0)
+ {
+ LOG_E("this tty have control process\n");
+
+ }
+ current->tty = tty;
+ current->tty_old_pgrp = 0;
+ tty->session = current->session;
+ tty->pgrp = current->__pgrp;
+ tty->foreground = current;
+ if (tty->type == TTY_DRIVER_TYPE_PTY)
+ {
+ tty->other_struct->foreground = current;
+ }
+ return 0;
+}
+
+static int tty_ioctl(struct dfs_fd *fd, int cmd, void *args)
+{
+ int ret = 0;
+ struct tty_struct *tty = RT_NULL;
+ struct tty_struct *real_tty = RT_NULL;
+ struct tty_ldisc *ld = RT_NULL;
+
+ tty = (struct tty_struct *)fd->vnode->data;
+ RT_ASSERT(tty != RT_NULL);
+
+ if (tty->type == TTY_DRIVER_TYPE_PTY && tty->subtype == PTY_TYPE_MASTER)
+ {
+ real_tty = tty->other_struct;
+ }
+ else
+ {
+ real_tty = tty;
+ }
+ switch (cmd)
+ {
+ case TIOCSCTTY:
+ return tiocsctty(real_tty, 1);
+ }
+ ld = tty->ldisc;
+ if (ld->ops->ioctl)
+ {
+ ret = ld->ops->ioctl(fd, cmd, args);
+ }
+ return ret;
+}
+
+static int tty_read(struct dfs_fd *fd, void *buf, size_t count)
+{
+ int ret = 0;
+ struct tty_struct *tty = RT_NULL;
+ struct tty_ldisc *ld = RT_NULL;
+
+ tty = (struct tty_struct *)fd->vnode->data;
+ RT_ASSERT(tty != RT_NULL);
+ ld = tty->ldisc;
+ if (ld->ops->read)
+ {
+ ret = ld->ops->read(fd, buf, count);
+ }
+ return ret;
+}
+
+static int tty_write(struct dfs_fd *fd, const void *buf, size_t count)
+{
+ int ret = 0;
+ struct tty_struct *tty = RT_NULL;
+ struct tty_ldisc *ld = RT_NULL;
+
+ tty = (struct tty_struct *)fd->vnode->data;
+ RT_ASSERT(tty != RT_NULL);
+ ld = tty->ldisc;
+ if (ld->ops->write)
+ {
+ ret = ld->ops->write(fd, buf, count);
+ }
+ return ret;
+}
+
+static int tty_poll(struct dfs_fd *fd, struct rt_pollreq *req)
+{
+ int ret = 0;
+ struct tty_struct *tty = RT_NULL;
+ struct tty_ldisc *ld = RT_NULL;
+
+ tty = (struct tty_struct *)fd->vnode->data;
+ RT_ASSERT(tty != RT_NULL);
+ ld = tty->ldisc;
+ if (ld->ops->poll)
+ {
+ ret = ld->ops->poll(fd, req);
+ }
+ return ret;
+}
+
+static const struct dfs_file_ops tty_fops =
+{
+ tty_open,
+ tty_close,
+ tty_ioctl,
+ tty_read,
+ tty_write,
+ RT_NULL, /* flush */
+ RT_NULL, /* lseek */
+ RT_NULL, /* getdents */
+ tty_poll,
+};
+static const struct dfs_file_ops console_fops =
+{
+ tty_open,
+ tty_close,
+ tty_ioctl,
+ tty_read,
+ tty_write,
+ RT_NULL, /* flush */
+ RT_NULL, /* lseek */
+ RT_NULL, /* getdents */
+ tty_poll,
+};
+
+void console_init()
+{
+ n_tty_init();
+}
+
+void tty_set_fops(struct dfs_file_ops *fops)
+{
+ *fops = tty_fops;
+}
+
+void console_set_fops(struct dfs_file_ops *fops)
+{
+ *fops = console_fops;
+}
diff --git a/components/drivers/tty/tty_ioctl.c b/components/drivers/tty/tty_ioctl.c
new file mode 100644
index 0000000000..f7e0d495da
--- /dev/null
+++ b/components/drivers/tty/tty_ioctl.c
@@ -0,0 +1,109 @@
+#include <stddef.h>
+#include <rtthread.h>
+#include <tty.h>
+#if defined(RT_USING_POSIX_DEVIO)
+#include <termios.h>
+#endif
+
+#define DBG_TAG "TTY_IOCTL"
+#ifdef RT_TTY_DEBUG
+#define DBG_LVL DBG_LOG
+#else
+#define DBG_LVL DBG_INFO
+#endif /* RT_TTY_DEBUG */
+#include <rtdbg.h>
+
+/*
+ * Internal flag options for termios setting behavior
+ */
+#define TERMIOS_FLUSH 1
+#define TERMIOS_WAIT 2
+#define TERMIOS_TERMIO 4
+#define TERMIOS_OLD 8
+
+/**
+ * set_termios - set termios values for a tty
+ * @tty: terminal device
+ * @arg: user data
+ * @opt: option information
+ *
+ * Helper function to prepare termios data and run necessary other
+ * functions before using tty_set_termios to do the actual changes.
+ *
+ * Locking:
+ * Called functions take ldisc and termios_rwsem locks
+ */
+
+static int set_termios(struct tty_struct *tty, void *arg, int opt)
+{
+ struct termios old_termios;
+ struct tty_ldisc *ld = RT_NULL;
+ struct termios *new_termios = (struct termios *)arg;
+ int level = 0;
+ int retval = tty_check_change(tty);
+
+ if (retval)
+ {
+ return retval;
+ }
+
+ rt_memcpy(&old_termios, &(tty->init_termios), sizeof(struct termios));
+ level = rt_hw_interrupt_disable();
+ tty->init_termios = *new_termios;
+ rt_hw_interrupt_enable(level);
+ ld = tty->ldisc;
+ if (ld != NULL)
+ {
+ if (ld->ops->set_termios)
+ {
+ ld->ops->set_termios(tty, &old_termios);
+ }
+ }
+ return 0;
+}
+
+int n_tty_ioctl_extend(struct tty_struct *tty, int cmd, void *args)
+{
+ int ret = 0;
+ void *p = (void *)args;
+ struct tty_struct *real_tty = RT_NULL;
+
+ if (tty->type == TTY_DRIVER_TYPE_PTY && tty->subtype == PTY_TYPE_MASTER)
+ {
+ real_tty = tty->other_struct;
+ }
+ else
+ {
+ real_tty = tty;
+ }
+
+ switch(cmd)
+ {
+ case TCGETS:
+ {
+ struct termios *tio = (struct termios *)p;
+ if (tio == RT_NULL)
+ {
+ return -RT_EINVAL;
+ }
+
+ rt_memcpy(tio, &real_tty->init_termios, sizeof(real_tty->init_termios));
+ return ret;
+ }
+ case TCSETSF:
+ {
+ return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_OLD);
+ }
+ case TCSETSW:
+ {
+ return set_termios(real_tty, p, TERMIOS_WAIT | TERMIOS_OLD);
+ }
+ case TCSETS:
+ {
+ return set_termios(real_tty, p, TERMIOS_OLD);
+ }
+ default:
+ break;
+ }
+ return -ENOIOCTLCMD;
+}
diff --git a/components/drivers/tty/tty_ldisc.c b/components/drivers/tty/tty_ldisc.c
new file mode 100644
index 0000000000..f736812414
--- /dev/null
+++ b/components/drivers/tty/tty_ldisc.c
@@ -0,0 +1,181 @@
+#include <tty.h>
+#include <tty_ldisc.h>
+
+#define DBG_TAG "TTY_LDISC"
+#ifdef RT_TTY_DEBUG
+#define DBG_LVL DBG_LOG
+#else
+#define DBG_LVL DBG_INFO
+#endif /* RT_TTY_DEBUG */
+#include <rtdbg.h>
+
+static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
+
+static struct tty_ldisc_ops *get_ldops(int disc)
+{
+ struct tty_ldisc_ops *ldops = RT_NULL;
+ int level = 0;
+ level = rt_hw_interrupt_disable();
+ ldops = tty_ldiscs[disc];
+ if (ldops)
+ {
+ ldops->refcount++;
+ }
+ rt_hw_interrupt_enable(level);
+ return ldops;
+}
+
+static void put_ldops(struct tty_ldisc_ops *ldops)
+{
+ int level = 0;
+
+ level = rt_hw_interrupt_disable();
+ ldops->refcount--;
+ rt_hw_interrupt_enable(level);
+}
+static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc)
+{
+ struct tty_ldisc *ld = RT_NULL;
+ struct tty_ldisc_ops *ldops = RT_NULL;
+
+ if (disc < N_TTY || disc >= NR_LDISCS)
+ {
+ return RT_NULL;
+ }
+
+ ldops = get_ldops(disc);
+ if (ldops == RT_NULL)
+ {
+ LOG_E("tty ldisc get error\n");
+ return RT_NULL;
+ }
+
+ ld = rt_malloc(sizeof(struct tty_ldisc));
+ if (ld == RT_NULL)
+ {
+ ldops->refcount--;
+ return RT_NULL;
+ }
+
+ ld->ops = ldops;
+ ld->tty = tty;
+
+ return ld;
+}
+
+/**
+ * tty_ldisc_put - release the ldisc
+ *
+ * Complement of tty_ldisc_get().
+ */
+static void tty_ldisc_put(struct tty_ldisc *ld)
+{
+ if (ld == RT_NULL)
+ {
+ return;
+ }
+
+ put_ldops(ld->ops);
+ rt_free(ld);
+}
+
+/**
+ * tty_ldisc_close - close a line discipline
+ * @tty: tty we are opening the ldisc on
+ * @ld: discipline to close
+ *
+ * A helper close method. Also a convenient debugging and check
+ * point.
+ */
+
+static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
+{
+ if (ld->ops->close)
+ {
+ ld->ops->close(tty);
+ }
+}
+
+/**
+ * tty_ldisc_kill - teardown ldisc
+ * @tty: tty being released
+ *
+ * Perform final close of the ldisc and reset tty->ldisc
+ */
+void tty_ldisc_kill(struct tty_struct *tty)
+{
+ if (!tty->ldisc)
+ {
+ return;
+ }
+
+ /*
+ * Now kill off the ldisc
+ */
+ tty_ldisc_close(tty, tty->ldisc);
+ tty_ldisc_put(tty->ldisc);
+ /* Force an oops if we mess this up */
+ tty->ldisc = NULL;
+}
+
+int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
+{
+ int ret = 0;
+ int level = 0;
+
+ if (disc < N_TTY || disc >= NR_LDISCS)
+ {
+ return -EINVAL;
+ }
+
+ level = rt_hw_interrupt_disable();
+ tty_ldiscs[disc] = new_ldisc;
+ new_ldisc->num = disc;
+ new_ldisc->refcount = 0;
+ rt_hw_interrupt_enable(level);
+
+ return ret;
+}
+
+/**
+ * tty_ldisc_release - release line discipline
+ * @tty: tty being shut down (or one end of pty pair)
+ *
+ * Called during the final close of a tty or a pty pair in order to shut
+ * down the line discpline layer. On exit, each tty's ldisc is NULL.
+ */
+
+void tty_ldisc_release(struct tty_struct *tty)
+{
+ int level = 0;
+ struct tty_struct *o_tty = tty->other_struct;
+
+ /*
+ * Shutdown this line discipline. As this is the final close,
+ * it does not race with the set_ldisc code path.
+ */
+
+ level = rt_hw_interrupt_disable();
+ tty_ldisc_kill(tty);
+ if (o_tty)
+ {
+ tty_ldisc_kill(o_tty);
+ }
+ rt_hw_interrupt_enable(level);
+
+}
+
+/**
+ * tty_ldisc_init - ldisc setup for new tty
+ * @tty: tty being allocated
+ *
+ * Set up the line discipline objects for a newly allocated tty. Note that
+ * the tty structure is not completely set up when this call is made.
+ */
+
+void tty_ldisc_init(struct tty_struct *tty)
+{
+ struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY);
+ RT_ASSERT(ld != RT_NULL);
+ tty->ldisc = ld;
+}
diff --git a/components/drivers/virtio/SConscript b/components/drivers/virtio/SConscript
new file mode 100644
index 0000000000..a7af6a017e
--- /dev/null
+++ b/components/drivers/virtio/SConscript
@@ -0,0 +1,11 @@
+# RT-Thread building script for component
+
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+CPPPATH = [cwd]
+
+group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_VIRTIO'], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/components/drivers/virtio/virtio.c b/components/drivers/virtio/virtio.c
new file mode 100644
index 0000000000..338b03ca94
--- /dev/null
+++ b/components/drivers/virtio/virtio.c
@@ -0,0 +1,324 @@
+/*
+ * 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 <cpuport.h>
+
+#include <virtio.h>
+
+rt_inline void _virtio_dev_check(struct virtio_device *dev)
+{
+ RT_ASSERT(dev != RT_NULL);
+ RT_ASSERT(dev->mmio_config != RT_NULL);
+}
+
+void virtio_reset_device(struct virtio_device *dev)
+{
+ _virtio_dev_check(dev);
+
+ dev->mmio_config->status = 0;
+}
+
+void virtio_status_acknowledge_driver(struct virtio_device *dev)
+{
+ _virtio_dev_check(dev);
+
+ dev->mmio_config->status |= VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER;
+}
+
+void virtio_status_driver_ok(struct virtio_device *dev)
+{
+ _virtio_dev_check(dev);
+
+ dev->mmio_config->status |= VIRTIO_STATUS_FEATURES_OK | VIRTIO_STATUS_DRIVER_OK;
+}
+
+void virtio_interrupt_ack(struct virtio_device *dev)
+{
+ rt_uint32_t status;
+
+ _virtio_dev_check(dev);
+
+ status = dev->mmio_config->interrupt_status;
+
+ if (status != 0)
+ {
+ dev->mmio_config->interrupt_ack = status;
+ }
+}
+
+rt_bool_t virtio_has_feature(struct virtio_device *dev, rt_uint32_t feature_bit)
+{
+ _virtio_dev_check(dev);
+
+ return !!(dev->mmio_config->device_features & (1UL << feature_bit));
+}
+
+rt_err_t virtio_queues_alloc(struct virtio_device *dev, rt_size_t queues_num)
+{
+ _virtio_dev_check(dev);
+
+ dev->queues = rt_malloc(sizeof(struct virtq) * queues_num);
+
+ if (dev->queues != RT_NULL)
+ {
+ dev->queues_num = queues_num;
+
+ return RT_EOK;
+ }
+
+ return -RT_ENOMEM;
+}
+
+void virtio_queues_free(struct virtio_device *dev)
+{
+ if (dev->queues != RT_NULL)
+ {
+ dev->queues_num = 0;
+ rt_free(dev->queues);
+ }
+}
+
+rt_err_t virtio_queue_init(struct virtio_device *dev, rt_uint32_t queue_index, rt_size_t ring_size)
+{
+ int i;
+ void *pages;
+ rt_size_t pages_total_size;
+ struct virtq *queue;
+
+ _virtio_dev_check(dev);
+
+ RT_ASSERT(dev->mmio_config->queue_num_max > 0);
+ RT_ASSERT(dev->mmio_config->queue_num_max > queue_index);
+ /* ring_size is power of 2 */
+ RT_ASSERT(ring_size > 0);
+ RT_ASSERT(((ring_size - 1) & ring_size) == 0);
+
+ queue = &dev->queues[queue_index];
+ pages_total_size = VIRTIO_PAGE_ALIGN(
+ VIRTQ_DESC_TOTAL_SIZE(ring_size) + VIRTQ_AVAIL_TOTAL_SIZE(ring_size)) + VIRTQ_USED_TOTAL_SIZE(ring_size);
+
+ pages = rt_malloc_align(pages_total_size, VIRTIO_PAGE_SIZE);
+
+ if (pages == RT_NULL)
+ {
+ return -RT_ENOMEM;
+ }
+
+ queue->free = rt_malloc(sizeof(rt_bool_t) * ring_size);
+
+ if (queue->free == RT_NULL)
+ {
+ rt_free_align(pages);
+ return -RT_ENOMEM;
+ }
+
+ rt_memset(pages, 0, pages_total_size);
+
+ dev->mmio_config->guest_page_size = VIRTIO_PAGE_SIZE;
+ dev->mmio_config->queue_sel = queue_index;
+ dev->mmio_config->queue_num = ring_size;
+ dev->mmio_config->queue_align = VIRTIO_PAGE_SIZE;
+ dev->mmio_config->queue_pfn = VIRTIO_VA2PA(pages) >> VIRTIO_PAGE_SHIFT;
+
+ queue->num = ring_size;
+ queue->desc = (struct virtq_desc *)((rt_ubase_t)pages);
+ queue->avail = (struct virtq_avail *)(((rt_ubase_t)pages) + VIRTQ_DESC_TOTAL_SIZE(ring_size));
+ queue->used = (struct virtq_used *)VIRTIO_PAGE_ALIGN(
+ (rt_ubase_t)&queue->avail->ring[ring_size] + VIRTQ_AVAIL_RES_SIZE);
+
+ queue->used_idx = 0;
+
+ /* All descriptors start out unused */
+ for (i = 0; i < ring_size; ++i)
+ {
+ queue->free[i] = RT_TRUE;
+ }
+
+ queue->free_count = ring_size;
+
+ return RT_EOK;
+}
+
+void virtio_queue_destroy(struct virtio_device *dev, rt_uint32_t queue_index)
+{
+ struct virtq *queue;
+
+ _virtio_dev_check(dev);
+
+ RT_ASSERT(dev->mmio_config->queue_num_max > 0);
+ RT_ASSERT(dev->mmio_config->queue_num_max > queue_index);
+
+ queue = &dev->queues[queue_index];
+
+ RT_ASSERT(queue->num > 0);
+
+ rt_free(queue->free);
+ rt_free_align((void *)queue->desc);
+
+ dev->mmio_config->queue_sel = queue_index;
+ dev->mmio_config->queue_pfn = RT_NULL;
+
+ queue->num = 0;
+ queue->desc = RT_NULL;
+ queue->avail = RT_NULL;
+ queue->used = RT_NULL;
+}
+
+void virtio_queue_notify(struct virtio_device *dev, rt_uint32_t queue_index)
+{
+ _virtio_dev_check(dev);
+
+ dev->mmio_config->queue_notify = queue_index;
+}
+
+void virtio_submit_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index)
+{
+ rt_size_t ring_size;
+ struct virtq *queue;
+
+ _virtio_dev_check(dev);
+
+ queue = &dev->queues[queue_index];
+ ring_size = queue->num;
+
+ /* Tell the device the first index in our chain of descriptors */
+ queue->avail->ring[queue->avail->idx % ring_size] = desc_index;
+ rt_hw_dsb();
+
+ /* Tell the device another avail ring entry is available */
+ queue->avail->idx++;
+ rt_hw_dsb();
+}
+
+rt_uint16_t virtio_alloc_desc(struct virtio_device *dev, rt_uint32_t queue_index)
+{
+ int i;
+ struct virtq *queue;
+
+ _virtio_dev_check(dev);
+
+ RT_ASSERT(queue_index < dev->queues_num);
+
+ queue = &dev->queues[queue_index];
+
+ if (queue->free_count > 0)
+ {
+ rt_size_t ring_size = queue->num;
+
+ for (i = 0; i < ring_size; ++i)
+ {
+ if (queue->free[i])
+ {
+ queue->free[i] = RT_FALSE;
+ queue->free_count--;
+
+ return (rt_uint16_t)i;
+ }
+ }
+ }
+
+ return VIRTQ_INVALID_DESC_ID;
+}
+
+void virtio_free_desc(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index)
+{
+ struct virtq *queue;
+
+ _virtio_dev_check(dev);
+
+ queue = &dev->queues[queue_index];
+
+ RT_ASSERT(queue_index < dev->queues_num);
+ RT_ASSERT(!queue->free[desc_index]);
+
+ queue->desc[desc_index].addr = 0;
+ queue->desc[desc_index].len = 0;
+ queue->desc[desc_index].flags = 0;
+ queue->desc[desc_index].next = 0;
+
+ queue->free[desc_index] = RT_TRUE;
+
+ queue->free_count++;
+}
+
+rt_err_t virtio_alloc_desc_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_size_t count,
+ rt_uint16_t *indexs)
+{
+ int i, j;
+
+ _virtio_dev_check(dev);
+
+ RT_ASSERT(indexs != RT_NULL);
+
+ if (dev->queues[queue_index].free_count < count)
+ {
+ return -RT_ERROR;
+ }
+
+ for (i = 0; i < count; ++i)
+ {
+ indexs[i] = virtio_alloc_desc(dev, queue_index);
+
+ if (indexs[i] == VIRTQ_INVALID_DESC_ID)
+ {
+ for (j = 0; j < i; ++j)
+ {
+ virtio_free_desc(dev, queue_index, indexs[j]);
+ }
+
+ return -RT_ERROR;
+ }
+ }
+
+ return RT_EOK;
+}
+
+void virtio_free_desc_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index)
+{
+ rt_uint16_t flags, next;
+ struct virtq_desc *desc;
+
+ _virtio_dev_check(dev);
+
+ desc = &dev->queues[queue_index].desc[0];
+
+ for (;;)
+ {
+ flags = desc[desc_index].flags;
+ next = desc[desc_index].next;
+
+ virtio_free_desc(dev, queue_index, desc_index);
+
+ if (flags & VIRTQ_DESC_F_NEXT)
+ {
+ desc_index = next;
+ }
+ else
+ {
+ break;
+ }
+ }
+}
+
+void virtio_fill_desc(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index,
+ rt_uint64_t addr, rt_uint32_t len, rt_uint16_t flags, rt_uint16_t next)
+{
+ struct virtq_desc *desc;
+
+ _virtio_dev_check(dev);
+
+ desc = &dev->queues[queue_index].desc[desc_index];
+
+ desc->addr = addr;
+ desc->len = len;
+ desc->flags = flags;
+ desc->next = next;
+}
diff --git a/components/drivers/virtio/virtio.h b/components/drivers/virtio/virtio.h
new file mode 100644
index 0000000000..297975601b
--- /dev/null
+++ b/components/drivers/virtio/virtio.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-9-16 GuEe-GUI the first version
+ * 2021-11-11 GuEe-GUI modify to virtio common interface
+ */
+
+#ifndef __VIRTIO_H__
+#define __VIRTIO_H__
+
+#include <rthw.h>
+#include <rtdef.h>
+
+#ifdef RT_USING_LWP
+#include <mmu.h>
+#include <ioremap.h>
+#endif
+
+#if RT_NAME_MAX < 16
+#error "Please set RT_NAME_MAX to at lest 16"
+#endif
+
+#ifdef RT_USING_VIRTIO10
+#define RT_USING_VIRTIO_VERSION 0x1
+#endif
+
+#include <virtio_mmio.h>
+#include <virtio_queue.h>
+
+#define VIRTIO_MAGIC_VALUE 0x74726976 /* "virt" */
+
+#define VIRTIO_STATUS_ACKNOWLEDGE (1 << 0)
+#define VIRTIO_STATUS_DRIVER (1 << 1)
+#define VIRTIO_STATUS_DRIVER_OK (1 << 2)
+#define VIRTIO_STATUS_FEATURES_OK (1 << 3)
+#define VIRTIO_STATUS_NEEDS_RESET (1 << 6)
+#define VIRTIO_STATUS_FAILED (1 << 7)
+
+#define VIRTIO_F_NOTIFY_ON_EMPTY 24
+#define VIRTIO_F_ANY_LAYOUT 27
+#define VIRTIO_F_RING_INDIRECT_DESC 28
+#define VIRTIO_F_RING_EVENT_IDX 29
+#define VIRTIO_F_VERSION_1 32
+#define VIRTIO_F_RING_PACKED 34
+
+#ifdef RT_USING_LWP
+extern rt_mmu_info mmu_info;
+#define VIRTIO_VA2PA(vaddr) ((rt_ubase_t)rt_hw_mmu_v2p(&mmu_info, vaddr))
+#define VIRTIO_PA2VA(paddr) ((rt_ubase_t)rt_ioremap((void *)paddr, ARCH_PAGE_SIZE))
+#else
+#define VIRTIO_VA2PA(vaddr) ((rt_ubase_t)vaddr)
+#define VIRTIO_PA2VA(paddr) ((rt_ubase_t)paddr)
+#endif /* RT_USING_LWP */
+#define VIRTIO_PAGE_SHIFT 12
+#define VIRTIO_PAGE_SIZE (1 << VIRTIO_PAGE_SHIFT)
+#define VIRTIO_PAGE_ALIGN(addr) (RT_ALIGN(addr, VIRTIO_PAGE_SIZE))
+
+enum
+{
+ /* virtio 1.0 */
+ VIRTIO_DEVICE_ID_INVALID = 0, /* Invalid device */
+ VIRTIO_DEVICE_ID_NET = 1, /* Net */
+ VIRTIO_DEVICE_ID_BLOCK = 2, /* Block */
+ VIRTIO_DEVICE_ID_CONSOLE = 3, /* Console */
+ VIRTIO_DEVICE_ID_RNG = 4, /* Rng */
+ VIRTIO_DEVICE_ID_BALLOON = 5, /* Balloon */
+ VIRTIO_DEVICE_ID_IOMEM = 6, /* IO memory */
+ VIRTIO_DEVICE_ID_RPMSG = 7, /* Remote processor messaging */
+ VIRTIO_DEVICE_ID_SCSI = 8, /* SCSI */
+ VIRTIO_DEVICE_ID_9P = 9, /* 9p console */
+ VIRTIO_DEVICE_ID_MAC80211_WLAN = 10, /* Mac80211 wlan */
+ VIRTIO_DEVICE_ID_RPROC_SERIAL = 11, /* Remoteproc serial link */
+ VIRTIO_DEVICE_ID_CAIF = 12, /* CAIF */
+ VIRTIO_DEVICE_ID_MEM_BALLOON = 13, /* Memory balloon */
+ VIRTIO_DEVICE_ID_GPU = 16, /* GPU */
+ VIRTIO_DEVICE_ID_TIME = 17, /* Timer/clock device */
+ VIRTIO_DEVICE_ID_INPUT = 18, /* Input */
+ /* virtio 1.1 */
+ VIRTIO_DEVICE_ID_SOCKET = 19, /* Socket device */
+ VIRTIO_DEVICE_ID_CRYPTO = 20, /* Crypto device */
+ VIRTIO_DEVICE_ID_SIG_DIS_MOD = 21, /* Signal Distribution Module */
+ VIRTIO_DEVICE_ID_PSTORE = 22, /* Pstore device */
+ VIRTIO_DEVICE_ID_IOMMU = 23, /* IOMMU device */
+ VIRTIO_DEVICE_ID_MEM = 24, /* Memory device */
+ /* virtio 1.2 */
+ VIRTIO_DEVICE_ID_AUDIO = 25, /* Audio device */
+ VIRTIO_DEVICE_ID_FS = 26, /* File system device */
+ VIRTIO_DEVICE_ID_PMEM = 27, /* PMEM device */
+ VIRTIO_DEVICE_ID_RPMB = 28, /* RPMB device */
+ VIRTIO_DEVICE_ID_MAC80211_HWSIM = 29, /* Mac80211 hwsim wireless simulation device */
+ VIRTIO_DEVICE_ID_VIDEO_ENCODER = 30, /* Video encoder device */
+ VIRTIO_DEVICE_ID_VIDEO_DECODER = 31, /* Video decoder device */
+ VIRTIO_DEVICE_ID_SCMI = 32, /* SCMI device */
+ VIRTIO_DEVICE_ID_NITRO_SEC_MOD = 33, /* NitroSecureModule */
+ VIRTIO_DEVICE_ID_I2C_ADAPTER = 34, /* I2C adapter */
+ VIRTIO_DEVICE_ID_WATCHDOG = 35, /* Watchdog */
+ VIRTIO_DEVICE_ID_CAN = 36, /* CAN device */
+ VIRTIO_DEVICE_ID_DMABUF = 37, /* Virtio dmabuf */
+ VIRTIO_DEVICE_ID_PARAM_SERV = 38, /* Parameter Server */
+ VIRTIO_DEVICE_ID_AUDIO_POLICY = 39, /* Audio policy device */
+ VIRTIO_DEVICE_ID_BT = 40, /* Bluetooth device */
+ VIRTIO_DEVICE_ID_GPIO = 41, /* GPIO device */
+ VIRTIO_DEVICE_ID_RDMA = 42, /* RDMA device */
+
+ VIRTIO_DEVICE_TYPE_SIZE
+};
+
+struct virtio_device
+{
+ rt_uint32_t irq;
+
+ struct virtq *queues;
+ rt_size_t queues_num;
+
+ union
+ {
+ rt_ubase_t *mmio_base;
+ struct virtio_mmio_config *mmio_config;
+ };
+
+#ifdef RT_USING_SMP
+ struct rt_spinlock spinlock;
+#endif
+
+ void *priv;
+};
+
+typedef rt_err_t (*virtio_device_init_handler)(rt_ubase_t *mmio_base, rt_uint32_t irq);
+
+void virtio_reset_device(struct virtio_device *dev);
+void virtio_status_acknowledge_driver(struct virtio_device *dev);
+void virtio_status_driver_ok(struct virtio_device *dev);
+void virtio_interrupt_ack(struct virtio_device *dev);
+rt_bool_t virtio_has_feature(struct virtio_device *dev, rt_uint32_t feature_bit);
+
+rt_err_t virtio_queues_alloc(struct virtio_device *dev, rt_size_t queues_num);
+void virtio_queues_free(struct virtio_device *dev);
+rt_err_t virtio_queue_init(struct virtio_device *dev, rt_uint32_t queue_index, rt_size_t ring_size);
+void virtio_queue_destroy(struct virtio_device *dev, rt_uint32_t queue_index);
+void virtio_queue_notify(struct virtio_device *dev, rt_uint32_t queue_index);
+
+void virtio_submit_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index);
+
+rt_uint16_t virtio_alloc_desc(struct virtio_device *dev, rt_uint32_t queue_index);
+void virtio_free_desc(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index);
+rt_err_t virtio_alloc_desc_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_size_t count,
+ rt_uint16_t *indexs);
+void virtio_free_desc_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index);
+void virtio_fill_desc(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index,
+ rt_uint64_t addr, rt_uint32_t len, rt_uint16_t flags, rt_uint16_t next);
+
+#endif /* __VIRTIO_H__ */
diff --git a/components/drivers/virtio/virtio_blk.c b/components/drivers/virtio/virtio_blk.c
new file mode 100644
index 0000000000..a568a7008a
--- /dev/null
+++ b/components/drivers/virtio/virtio_blk.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-9-16 GuEe-GUI the first version
+ * 2021-11-11 GuEe-GUI using virtio common interface
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <cpuport.h>
+
+#ifdef RT_USING_VIRTIO_BLK
+
+#include <virtio_blk.h>
+
+static void virtio_blk_rw(struct virtio_blk_device *virtio_blk_dev, rt_off_t pos, void *buffer, rt_size_t count,
+ int flags)
+{
+ rt_uint16_t idx[3];
+ rt_size_t size = count * virtio_blk_dev->config->blk_size;
+ struct virtio_device *virtio_dev = &virtio_blk_dev->virtio_dev;
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+
+ /* Allocate 3 descriptors */
+ while (virtio_alloc_desc_chain(virtio_dev, 0, 3, idx))
+ {
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+ rt_thread_yield();
+
+#ifdef RT_USING_SMP
+ level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+ }
+
+ virtio_blk_dev->info[idx[0]].status = 0xff;
+ virtio_blk_dev->info[idx[0]].valid = RT_TRUE;
+ virtio_blk_dev->info[idx[0]].req.type = flags;
+ virtio_blk_dev->info[idx[0]].req.ioprio = 0;
+ virtio_blk_dev->info[idx[0]].req.sector = pos * (virtio_blk_dev->config->blk_size / 512);
+
+ flags = flags == VIRTIO_BLK_T_OUT ? 0 : VIRTQ_DESC_F_WRITE;
+
+ virtio_fill_desc(virtio_dev, VIRTIO_BLK_QUEUE, idx[0],
+ VIRTIO_VA2PA(&virtio_blk_dev->info[idx[0]].req), sizeof(struct virtio_blk_req), VIRTQ_DESC_F_NEXT, idx[1]);
+
+ virtio_fill_desc(virtio_dev, VIRTIO_BLK_QUEUE, idx[1],
+ VIRTIO_VA2PA(buffer), size, flags | VIRTQ_DESC_F_NEXT, idx[2]);
+
+ virtio_fill_desc(virtio_dev, VIRTIO_BLK_QUEUE, idx[2],
+ VIRTIO_VA2PA(&virtio_blk_dev->info[idx[0]].status), sizeof(rt_uint8_t), VIRTQ_DESC_F_WRITE, 0);
+
+ virtio_submit_chain(virtio_dev, VIRTIO_BLK_QUEUE, idx[0]);
+
+ virtio_queue_notify(virtio_dev, VIRTIO_BLK_QUEUE);
+
+ /* Wait for virtio_blk_isr() to done */
+ while (virtio_blk_dev->info[idx[0]].valid)
+ {
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+ rt_thread_yield();
+
+#ifdef RT_USING_SMP
+ level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+ }
+
+ virtio_free_desc_chain(virtio_dev, VIRTIO_BLK_QUEUE, idx[0]);
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+}
+
+static rt_size_t virtio_blk_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t count)
+{
+ virtio_blk_rw((struct virtio_blk_device *)dev, pos, buffer, count, VIRTIO_BLK_T_IN);
+
+ return count;
+}
+
+static rt_size_t virtio_blk_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t count)
+{
+ virtio_blk_rw((struct virtio_blk_device *)dev, pos, (void *)buffer, count, VIRTIO_BLK_T_OUT);
+
+ return count;
+}
+
+static rt_err_t virtio_blk_control(rt_device_t dev, int cmd, void *args)
+{
+ rt_err_t status = RT_EOK;
+ struct virtio_blk_device *virtio_blk_dev = (struct virtio_blk_device *)dev;
+
+ switch (cmd)
+ {
+ case RT_DEVICE_CTRL_BLK_GETGEOME:
+ {
+ struct rt_device_blk_geometry *geometry = (struct rt_device_blk_geometry *)args;
+
+ if (geometry == RT_NULL)
+ {
+ status = -RT_ERROR;
+ break;
+ }
+
+ geometry->bytes_per_sector = VIRTIO_BLK_BYTES_PER_SECTOR;
+ geometry->block_size = virtio_blk_dev->config->blk_size;
+ geometry->sector_count = virtio_blk_dev->config->capacity;
+ }
+ break;
+ default:
+ status = -RT_EINVAL;
+ break;
+ }
+
+ return status;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops virtio_blk_ops =
+{
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ virtio_blk_read,
+ virtio_blk_write,
+ virtio_blk_control
+};
+#endif
+
+static void virtio_blk_isr(int irqno, void *param)
+{
+ rt_uint32_t id;
+ struct virtio_blk_device *virtio_blk_dev = (struct virtio_blk_device *)param;
+ struct virtio_device *virtio_dev = &virtio_blk_dev->virtio_dev;
+ struct virtq *queue = &virtio_dev->queues[VIRTIO_BLK_QUEUE];
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+
+ virtio_interrupt_ack(virtio_dev);
+ rt_hw_dsb();
+
+ /* The device increments disk.used->idx when it adds an entry to the used ring */
+ while (queue->used_idx != queue->used->idx)
+ {
+ rt_hw_dsb();
+ id = queue->used->ring[queue->used_idx % queue->num].id;
+
+ RT_ASSERT(virtio_blk_dev->info[id].status == 0);
+
+ /* Done with buffer */
+ virtio_blk_dev->info[id].valid = RT_FALSE;
+
+ queue->used_idx++;
+ }
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+}
+
+rt_err_t rt_virtio_blk_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
+{
+ static int dev_no = 0;
+ char dev_name[RT_NAME_MAX];
+ struct virtio_device *virtio_dev;
+ struct virtio_blk_device *virtio_blk_dev;
+
+ virtio_blk_dev = rt_malloc(sizeof(struct virtio_blk_device));
+
+ if (virtio_blk_dev == RT_NULL)
+ {
+ return -RT_ENOMEM;
+ }
+
+ virtio_dev = &virtio_blk_dev->virtio_dev;
+ virtio_dev->irq = irq;
+ virtio_dev->mmio_base = mmio_base;
+
+ virtio_blk_dev->config = (struct virtio_blk_config *)virtio_dev->mmio_config->config;
+
+#ifdef RT_USING_SMP
+ rt_spin_lock_init(&virtio_dev->spinlock);
+#endif
+
+ virtio_reset_device(virtio_dev);
+ virtio_status_acknowledge_driver(virtio_dev);
+
+ /* Negotiate features */
+ virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~(
+ (1 << VIRTIO_BLK_F_RO) |
+ (1 << VIRTIO_BLK_F_MQ) |
+ (1 << VIRTIO_BLK_F_SCSI) |
+ (1 << VIRTIO_BLK_F_CONFIG_WCE) |
+ (1 << VIRTIO_F_ANY_LAYOUT) |
+ (1 << VIRTIO_F_RING_EVENT_IDX) |
+ (1 << VIRTIO_F_RING_INDIRECT_DESC));
+
+ /* Tell device that feature negotiation is complete and we're completely ready */
+ virtio_status_driver_ok(virtio_dev);
+
+ if (virtio_queues_alloc(virtio_dev, 1) != RT_EOK)
+ {
+ goto _alloc_fail;
+ }
+
+ /* Initialize queue 0 */
+ if (virtio_queue_init(virtio_dev, 0, VIRTIO_BLK_QUEUE_RING_SIZE) != RT_EOK)
+ {
+ goto _alloc_fail;
+ }
+
+ virtio_blk_dev->parent.type = RT_Device_Class_Block;
+#ifdef RT_USING_DEVICE_OPS
+ virtio_blk_dev->parent.ops = &virtio_blk_ops;
+#else
+ virtio_blk_dev->parent.init = RT_NULL;
+ virtio_blk_dev->parent.open = RT_NULL;
+ virtio_blk_dev->parent.close = RT_NULL;
+ virtio_blk_dev->parent.read = virtio_blk_read;
+ virtio_blk_dev->parent.write = virtio_blk_write;
+ virtio_blk_dev->parent.control = virtio_blk_control;
+#endif
+
+ rt_snprintf(dev_name, RT_NAME_MAX, "virtio-blk%d", dev_no++);
+
+ rt_hw_interrupt_install(irq, virtio_blk_isr, virtio_blk_dev, dev_name);
+ rt_hw_interrupt_umask(irq);
+
+ return rt_device_register((rt_device_t)virtio_blk_dev, dev_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE);
+
+_alloc_fail:
+
+ if (virtio_blk_dev != RT_NULL)
+ {
+ virtio_queues_free(virtio_dev);
+ rt_free(virtio_blk_dev);
+ }
+ return -RT_ENOMEM;
+}
+#endif /* RT_USING_VIRTIO_BLK */
diff --git a/components/drivers/virtio/virtio_blk.h b/components/drivers/virtio/virtio_blk.h
new file mode 100644
index 0000000000..548bcee4c8
--- /dev/null
+++ b/components/drivers/virtio/virtio_blk.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-9-16 GuEe-GUI the first version
+ * 2021-11-11 GuEe-GUI using virtio common interface
+ */
+
+#ifndef __VIRTIO_BLK_H__
+#define __VIRTIO_BLK_H__
+
+#include <rtdef.h>
+
+#include <virtio.h>
+
+#define VIRTIO_BLK_QUEUE 0
+#define VIRTIO_BLK_BYTES_PER_SECTOR 512
+#define VIRTIO_BLK_QUEUE_RING_SIZE 4
+
+#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */
+#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */
+#define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */
+#define VIRTIO_BLK_F_MQ 12 /* Support more than one vq */
+
+#define VIRTIO_BLK_T_IN 0 /* Read the blk */
+#define VIRTIO_BLK_T_OUT 1 /* Write the blk */
+#define VIRTIO_BLK_T_SCSI_CMD 2
+#define VIRTIO_BLK_T_SCSI_CMD_OUT 3
+#define VIRTIO_BLK_T_FLUSH 4
+#define VIRTIO_BLK_T_FLUSH_OUT 5
+
+struct virtio_blk_req
+{
+ rt_uint32_t type;
+ rt_uint32_t ioprio;
+ rt_uint64_t sector;
+};
+
+struct virtio_blk_config
+{
+ rt_uint64_t capacity; /* The capacity (in 512-byte sectors). */
+ rt_uint32_t size_max; /* The maximum segment size (if VIRTIO_BLK_F_SIZE_MAX) */
+ rt_uint32_t seg_max; /* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */
+
+ /* Geometry of the device (if VIRTIO_BLK_F_GEOMETRY) */
+ struct virtio_blk_geometry
+ {
+ rt_uint16_t cylinders;
+ rt_uint8_t heads;
+ rt_uint8_t sectors;
+ } geometry;
+
+ rt_uint32_t blk_size; /* Block size of device (if VIRTIO_BLK_F_BLK_SIZE) */
+
+ struct virtio_blk_topology
+ {
+ /* # Of logical blocks per physical block (log2) */
+ rt_uint8_t physical_block_exp;
+ /* Offset of first aligned logical block */
+ rt_uint8_t alignment_offset;
+ /* Suggested minimum I/O size in blocks */
+ rt_uint16_t min_io_size;
+ /* Optimal (suggested maximum) I/O size in blocks */
+ rt_uint32_t opt_io_size;
+ } topology;
+
+ rt_uint8_t writeback;
+ rt_uint8_t unused0;
+ rt_uint16_t num_queues;
+ rt_uint32_t max_discard_sectors;
+ rt_uint32_t max_discard_seg;
+ rt_uint32_t discard_sector_alignment;
+ rt_uint32_t max_write_zeroes_sectors;
+ rt_uint32_t max_write_zeroes_seg;
+ rt_uint8_t write_zeroes_may_unmap;
+ rt_uint8_t unused1[3];
+ rt_uint32_t max_secure_erase_sectors;
+ rt_uint32_t max_secure_erase_seg;
+ rt_uint32_t secure_erase_sector_alignment;
+} __attribute__((packed));
+
+struct virtio_blk_device
+{
+ struct rt_device parent;
+
+ struct virtio_device virtio_dev;
+
+ struct virtio_blk_config *config;
+
+ struct
+ {
+ rt_bool_t valid;
+ rt_uint8_t status;
+
+ struct virtio_blk_req req;
+
+ } info[VIRTIO_BLK_QUEUE_RING_SIZE];
+};
+
+rt_err_t rt_virtio_blk_init(rt_ubase_t *mmio_base, rt_uint32_t irq);
+
+#endif /* __VIRTIO_BLK_H__ */
diff --git a/components/drivers/virtio/virtio_console.c b/components/drivers/virtio/virtio_console.c
new file mode 100644
index 0000000000..7f1a9b8af5
--- /dev/null
+++ b/components/drivers/virtio/virtio_console.c
@@ -0,0 +1,778 @@
+/*
+ * 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 <rthw.h>
+#include <rtthread.h>
+#include <cpuport.h>
+
+#ifdef RT_USING_VIRTIO_CONSOLE
+
+#include <virtio_console.h>
+
+struct port_device
+{
+ struct rt_device parent;
+
+ rt_list_t node;
+ rt_uint32_t port_id;
+ rt_bool_t rx_notify;
+ rt_bool_t need_destroy;
+
+ struct virtio_console_device *console;
+
+ struct virtq *queue_rx, *queue_tx;
+ rt_uint32_t queue_rx_index, queue_tx_index;
+
+#ifdef RT_USING_SMP
+ struct rt_spinlock spinlock_rx, spinlock_tx;
+#endif
+
+ struct rt_device_notify rx_notify_helper;
+
+ struct
+ {
+ char rx_char, tx_char;
+ } info[VIRTIO_CONSOLE_QUEUE_SIZE];
+};
+
+static void virtio_console_send_ctrl(struct virtio_console_device *virtio_console_dev,
+ struct virtio_console_control *ctrl)
+{
+ rt_uint16_t id;
+ struct virtio_device *virtio_dev = &virtio_console_dev->virtio_dev;
+ struct virtq *queue_ctrl_tx;
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+
+ queue_ctrl_tx = &virtio_dev->queues[VIRTIO_CONSOLE_QUEUE_CTRL_TX];
+
+ id = queue_ctrl_tx->avail->idx % queue_ctrl_tx->num;
+
+ rt_memcpy(&virtio_console_dev->info[id].tx_ctrl, ctrl, sizeof(struct virtio_console_control));
+
+ virtio_free_desc(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_TX, id);
+
+ virtio_fill_desc(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_TX, id,
+ virtio_console_dev->info[id].tx_ctrl_paddr, sizeof(struct virtio_console_control), 0, 0);
+
+ virtio_submit_chain(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_TX, id);
+
+ virtio_queue_notify(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_TX);
+
+ virtio_alloc_desc(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_TX);
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+}
+
+static rt_err_t virtio_console_port_init(rt_device_t dev);
+static rt_err_t virtio_console_port_open(rt_device_t dev, rt_uint16_t oflag);
+static rt_err_t virtio_console_port_close(rt_device_t dev);
+static rt_size_t virtio_console_port_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
+static rt_size_t virtio_console_port_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);
+static rt_err_t virtio_console_port_control(rt_device_t dev, int cmd, void *args);
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops virtio_console_port_ops =
+{
+ virtio_console_port_init,
+ virtio_console_port_open,
+ virtio_console_port_close,
+ virtio_console_port_read,
+ virtio_console_port_write,
+ virtio_console_port_control
+};
+#endif
+
+static rt_err_t virtio_console_port_create(struct virtio_console_device *virtio_console_dev)
+{
+ rt_uint32_t port_id;
+ char dev_name[RT_NAME_MAX];
+ struct port_device *port_dev, *prev_port_dev = RT_NULL;
+ struct virtio_device *virtio_dev = &virtio_console_dev->virtio_dev;
+
+ if (virtio_console_dev->port_nr > 0 && !virtio_has_feature(virtio_dev, VIRTIO_CONSOLE_F_MULTIPORT))
+ {
+ return -RT_ENOSYS;
+ }
+
+ if (virtio_console_dev->port_nr >= virtio_console_dev->max_port_nr)
+ {
+ return -RT_EFULL;
+ }
+
+ port_id = 0;
+
+ /* The port device list is always ordered, so just find next number for id */
+ rt_list_for_each_entry(port_dev, &virtio_console_dev->port_head, node)
+ {
+ if (port_dev->port_id != port_id)
+ {
+ break;
+ }
+ ++port_id;
+ prev_port_dev = port_dev;
+ }
+
+ port_dev = rt_malloc(sizeof(struct port_device));
+
+ if (port_dev == RT_NULL)
+ {
+ return -RT_ENOMEM;
+ }
+
+ port_dev->parent.type = RT_Device_Class_Char;
+#ifdef RT_USING_DEVICE_OPS
+ port_dev->parent.ops = &virtio_console_port_ops;
+#else
+ port_dev->parent.init = virtio_console_port_init;
+ port_dev->parent.open = virtio_console_port_open;
+ port_dev->parent.close = virtio_console_port_close;
+ port_dev->parent.read = virtio_console_port_read;
+ port_dev->parent.write = virtio_console_port_write;
+ port_dev->parent.control = virtio_console_port_control;
+#endif
+
+ port_dev->parent.rx_indicate = RT_NULL;
+ port_dev->parent.tx_complete = RT_NULL;
+
+ rt_list_init(&port_dev->node);
+ port_dev->port_id = port_id;
+ port_dev->need_destroy = RT_FALSE;
+ port_dev->rx_notify = RT_TRUE;
+ port_dev->console = virtio_console_dev;
+ port_dev->queue_rx_index = VIRTIO_CONSOLE_PORT_QUEUE_INDEX(port_dev->port_id, VIRTIO_CONSOLE_QUEUE_DATA_RX);
+ port_dev->queue_tx_index = VIRTIO_CONSOLE_PORT_QUEUE_INDEX(port_dev->port_id, VIRTIO_CONSOLE_QUEUE_DATA_TX);
+ port_dev->queue_rx = &virtio_dev->queues[port_dev->queue_rx_index];
+ port_dev->queue_tx = &virtio_dev->queues[port_dev->queue_tx_index];
+
+#ifdef RT_USING_SMP
+ rt_spin_lock_init(&port_dev->spinlock_rx);
+ rt_spin_lock_init(&port_dev->spinlock_tx);
+#endif
+
+ rt_snprintf(dev_name, RT_NAME_MAX, "vport%dp%d", virtio_console_dev->console_id, port_id);
+
+ if (rt_device_register((rt_device_t)port_dev, dev_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX) != RT_EOK)
+ {
+ rt_free(port_dev);
+
+ return -RT_ERROR;
+ }
+
+ if (prev_port_dev != RT_NULL)
+ {
+ rt_list_insert_after(&prev_port_dev->node, &port_dev->node);
+ }
+ else
+ {
+ /* Port0 */
+ rt_list_insert_after(&virtio_console_dev->port_head, &port_dev->node);
+ }
+
+ virtio_console_dev->port_nr++;
+
+ return RT_EOK;
+}
+
+static void virtio_console_port_destroy(struct virtio_console_device *virtio_console_dev,
+ struct port_device *port_dev)
+{
+ struct virtio_console_control set_ctrl;
+
+ set_ctrl.id = port_dev->port_id;
+ set_ctrl.event = VIRTIO_CONSOLE_PORT_OPEN;
+ set_ctrl.value = 0;
+
+ virtio_console_send_ctrl(virtio_console_dev, &set_ctrl);
+
+ virtio_console_dev->port_nr--;
+
+ rt_list_remove(&port_dev->node);
+
+ rt_device_unregister((rt_device_t)port_dev);
+
+ rt_free(port_dev);
+}
+
+static rt_err_t virtio_console_port_init(rt_device_t dev)
+{
+ rt_uint16_t id;
+ rt_uint16_t idx[VIRTIO_CONSOLE_QUEUE_SIZE];
+ rt_uint16_t rx_queue_index, tx_queue_index;
+ struct port_device *port_dev = (struct port_device *)dev;
+ struct virtio_console_device *virtio_console_dev = port_dev->console;
+ struct virtio_device *virtio_dev = &virtio_console_dev->virtio_dev;
+ struct virtq *queue_rx, *queue_tx;
+
+ rx_queue_index = VIRTIO_CONSOLE_PORT_QUEUE_INDEX(port_dev->port_id, VIRTIO_CONSOLE_QUEUE_DATA_RX);
+ tx_queue_index = VIRTIO_CONSOLE_PORT_QUEUE_INDEX(port_dev->port_id, VIRTIO_CONSOLE_QUEUE_DATA_TX);
+
+ queue_rx = &virtio_dev->queues[rx_queue_index];
+ queue_tx = &virtio_dev->queues[tx_queue_index];
+
+ virtio_alloc_desc_chain(virtio_dev, rx_queue_index, queue_rx->num, idx);
+ virtio_alloc_desc_chain(virtio_dev, tx_queue_index, queue_tx->num, idx);
+
+ for (id = 0; id < queue_rx->num; ++id)
+ {
+ void *addr = &port_dev->info[id].rx_char;
+
+ virtio_fill_desc(virtio_dev, rx_queue_index, id,
+ VIRTIO_VA2PA(addr), sizeof(char), VIRTQ_DESC_F_WRITE, 0);
+
+ queue_rx->avail->ring[id] = id;
+ }
+ rt_hw_dsb();
+
+ queue_rx->avail->flags = 0;
+ queue_rx->avail->idx = queue_rx->num;
+
+ queue_rx->used_idx = queue_rx->used->idx;
+
+ queue_tx->avail->flags = VIRTQ_AVAIL_F_NO_INTERRUPT;
+ queue_tx->avail->idx = 0;
+
+ virtio_queue_notify(virtio_dev, rx_queue_index);
+
+ if (virtio_has_feature(virtio_dev, VIRTIO_CONSOLE_F_MULTIPORT))
+ {
+ struct virtio_console_control set_ctrl;
+
+ set_ctrl.id = VIRTIO_CONSOLE_PORT_BAD_ID;
+ set_ctrl.event = VIRTIO_CONSOLE_DEVICE_READY;
+ set_ctrl.value = 1;
+
+ virtio_console_send_ctrl(virtio_console_dev, &set_ctrl);
+ }
+
+ return RT_EOK;
+}
+
+static rt_err_t virtio_console_port_open(rt_device_t dev, rt_uint16_t oflag)
+{
+ struct port_device *port_dev = (struct port_device *)dev;
+
+ if (port_dev->port_id == 0 && virtio_has_feature(&port_dev->console->virtio_dev, VIRTIO_CONSOLE_F_MULTIPORT))
+ {
+ /* Port0 is reserve in multiport */
+ return -RT_ERROR;
+ }
+
+ port_dev->rx_notify = RT_TRUE;
+
+ return RT_EOK;
+}
+
+static rt_err_t virtio_console_port_close(rt_device_t dev)
+{
+ struct port_device *port_dev = (struct port_device *)dev;
+
+ if (port_dev->need_destroy)
+ {
+ virtio_console_port_destroy(port_dev->console, port_dev);
+
+ /*
+ * We released the device memory in virtio_console_port_destroy,
+ * rt_device_close has not finished yet, make the return value
+ * to empty so that rt_device_close will not access the device memory.
+ */
+ return -RT_EEMPTY;
+ }
+
+ port_dev->rx_notify = RT_FALSE;
+
+ return RT_EOK;
+}
+
+static rt_size_t virtio_console_port_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+ rt_off_t i = 0;
+ rt_uint16_t id;
+ rt_uint32_t len;
+ struct port_device *port_dev = (struct port_device *)dev;
+ struct virtio_device *virtio_dev = &port_dev->console->virtio_dev;
+ rt_uint32_t queue_rx_index = port_dev->queue_rx_index;
+ struct virtq *queue_rx = port_dev->queue_rx;
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&port_dev->spinlock_rx);
+#endif
+
+ while (i < size)
+ {
+ if (queue_rx->used_idx == queue_rx->used->idx)
+ {
+ break;
+ }
+ rt_hw_dsb();
+
+ id = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].id;
+ len = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].len;
+
+ if (len > sizeof(char))
+ {
+ rt_kprintf("%s: Receive buffer's size = %u is too big!\n", port_dev->parent.parent.name, len);
+ len = sizeof(char);
+ }
+
+ *((char *)buffer + i) = port_dev->info[id].rx_char;
+
+ queue_rx->used_idx++;
+
+ virtio_submit_chain(virtio_dev, queue_rx_index, id);
+
+ virtio_queue_notify(virtio_dev, queue_rx_index);
+
+ i += len;
+ }
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&port_dev->spinlock_rx, level);
+#endif
+
+ size = i;
+
+ return size;
+}
+
+static rt_size_t virtio_console_port_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+ char ch = 0;
+ rt_off_t i = 0;
+ rt_uint16_t id;
+ struct port_device *port_dev = (struct port_device *)dev;
+ struct virtio_device *virtio_dev = &port_dev->console->virtio_dev;
+ rt_uint32_t queue_tx_index = port_dev->queue_tx_index;
+ struct virtq *queue_tx = port_dev->queue_tx;
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&port_dev->spinlock_tx);
+#endif
+
+ while (i < size || ch == '\r')
+ {
+ id = queue_tx->avail->idx % queue_tx->num;
+
+ /* Keep the way until 'new line' are unified */
+ if (ch != '\r')
+ {
+ ch = *((const char *)buffer + i);
+ }
+ else
+ {
+ i -= sizeof(char);
+ }
+
+ port_dev->info[id].tx_char = ch;
+
+ ch = (ch == '\n' ? '\r' : 0);
+
+ virtio_free_desc(virtio_dev, queue_tx_index, id);
+
+ virtio_fill_desc(virtio_dev, queue_tx_index, id,
+ VIRTIO_VA2PA(&port_dev->info[id].tx_char), sizeof(char), 0, 0);
+
+ virtio_submit_chain(virtio_dev, queue_tx_index, id);
+
+ virtio_queue_notify(virtio_dev, queue_tx_index);
+
+ virtio_alloc_desc(virtio_dev, queue_tx_index);
+
+ i += sizeof(char);
+ }
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&port_dev->spinlock_tx, level);
+#endif
+
+ return size;
+}
+
+static rt_err_t virtio_console_port_control(rt_device_t dev, int cmd, void *args)
+{
+ rt_err_t status = RT_EOK;
+ struct port_device *port_dev = (struct port_device *)dev;
+
+ switch (cmd)
+ {
+ case RT_DEVICE_CTRL_NOTIFY_SET:
+ if (args == RT_NULL)
+ {
+ status = -RT_ERROR;
+ break;
+ }
+ rt_memcpy(&port_dev->rx_notify_helper, args, sizeof(port_dev->rx_notify_helper));
+ break;
+ case RT_DEVICE_CTRL_CLR_INT:
+ /* Disable RX */
+ port_dev->rx_notify = RT_FALSE;
+ break;
+ case RT_DEVICE_CTRL_SET_INT:
+ /* Enable RX */
+ port_dev->rx_notify = RT_TRUE;
+ break;
+ case VIRTIO_DEVICE_CTRL_CONSOLE_PORT_DESTROY:
+ {
+ port_dev->need_destroy = RT_TRUE;
+ port_dev->rx_notify = RT_FALSE;
+ }
+ break;
+ default:
+ status = -RT_EINVAL;
+ break;
+ }
+
+ return status;
+}
+
+static rt_err_t virtio_console_init(rt_device_t dev)
+{
+ struct virtio_console_device *virtio_console_dev = (struct virtio_console_device *)dev;
+ struct virtio_device *virtio_dev = &virtio_console_dev->virtio_dev;
+
+ if (virtio_has_feature(virtio_dev, VIRTIO_CONSOLE_F_MULTIPORT))
+ {
+ rt_uint16_t id;
+ rt_uint16_t idx[VIRTIO_CONSOLE_QUEUE_SIZE];
+ struct virtq *queue_ctrl_rx, *queue_ctrl_tx;
+
+ queue_ctrl_rx = &virtio_dev->queues[VIRTIO_CONSOLE_QUEUE_CTRL_RX];
+ queue_ctrl_tx = &virtio_dev->queues[VIRTIO_CONSOLE_QUEUE_CTRL_TX];
+
+ virtio_alloc_desc_chain(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_RX, queue_ctrl_rx->num, idx);
+ virtio_alloc_desc_chain(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_TX, queue_ctrl_tx->num, idx);
+
+ for (id = 0; id < queue_ctrl_rx->num; ++id)
+ {
+ void *addr = &virtio_console_dev->info[id].rx_ctrl;
+
+ virtio_fill_desc(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_RX, id,
+ VIRTIO_VA2PA(addr), sizeof(struct virtio_console_control), VIRTQ_DESC_F_WRITE, 0);
+
+ queue_ctrl_rx->avail->ring[id] = id;
+ }
+ rt_hw_dsb();
+
+ for (id = 0; id < queue_ctrl_tx->num; ++id)
+ {
+ virtio_console_dev->info[id].tx_ctrl_paddr = VIRTIO_VA2PA(&virtio_console_dev->info[id].tx_ctrl);
+ }
+
+ queue_ctrl_rx->avail->flags = 0;
+ queue_ctrl_rx->avail->idx = queue_ctrl_rx->num;
+
+ queue_ctrl_rx->used_idx = queue_ctrl_rx->used->idx;
+
+ queue_ctrl_tx->avail->flags = VIRTQ_AVAIL_F_NO_INTERRUPT;
+ queue_ctrl_tx->avail->idx = 0;
+
+ virtio_queue_notify(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_RX);
+ }
+
+ return virtio_console_port_create(virtio_console_dev);
+}
+
+static rt_err_t virtio_console_control(rt_device_t dev, int cmd, void *args)
+{
+ rt_err_t status = RT_EOK;
+ struct virtio_console_device *virtio_console_dev = (struct virtio_console_device *)dev;
+
+ switch (cmd)
+ {
+ case VIRTIO_DEVICE_CTRL_CONSOLE_PORT_CREATE:
+ status = virtio_console_port_create(virtio_console_dev);
+ break;
+ default:
+ status = -RT_EINVAL;
+ break;
+ }
+
+ return status;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops virtio_console_ops =
+{
+ virtio_console_init,
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ virtio_console_control
+};
+#endif
+
+static void virtio_console_isr(int irqno, void *param)
+{
+ rt_uint32_t id;
+ rt_uint32_t len;
+ struct port_device *port_dev;
+ struct virtio_console_device *virtio_console_dev = (struct virtio_console_device *)param;
+ struct virtio_device *virtio_dev = &virtio_console_dev->virtio_dev;
+ const char *dev_name = virtio_console_dev->parent.parent.name;
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+
+ virtio_interrupt_ack(virtio_dev);
+ rt_hw_dsb();
+
+ do {
+ struct virtq *queue_rx;
+ struct virtio_console_control *ctrl, set_ctrl;
+
+ if (!virtio_has_feature(virtio_dev, VIRTIO_CONSOLE_F_MULTIPORT))
+ {
+ break;
+ }
+
+ queue_rx = &virtio_dev->queues[VIRTIO_CONSOLE_QUEUE_CTRL_RX];
+
+ if (queue_rx->used_idx == queue_rx->used->idx)
+ {
+ break;
+ }
+ rt_hw_dsb();
+
+ id = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].id;
+ len = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].len;
+
+ queue_rx->used_idx++;
+
+ if (len != sizeof(struct virtio_console_control))
+ {
+ rt_kprintf("%s: Invalid ctrl!\n", dev_name);
+ break;
+ }
+
+ ctrl = &virtio_console_dev->info[id].rx_ctrl;
+
+ switch (ctrl->event)
+ {
+ case VIRTIO_CONSOLE_PORT_ADD:
+ {
+ set_ctrl.id = ctrl->id;
+ set_ctrl.event = VIRTIO_CONSOLE_PORT_READY;
+ set_ctrl.value = 1;
+
+ #ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+ #endif
+
+ virtio_console_send_ctrl(virtio_console_dev, &set_ctrl);
+
+ #ifdef RT_USING_SMP
+ level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+ #endif
+ }
+ break;
+ case VIRTIO_CONSOLE_PORT_REMOVE:
+ break;
+ case VIRTIO_CONSOLE_RESIZE:
+ break;
+ case VIRTIO_CONSOLE_PORT_OPEN:
+ {
+ set_ctrl.id = ctrl->id;
+ set_ctrl.event = VIRTIO_CONSOLE_PORT_OPEN;
+ set_ctrl.value = 1;
+
+ #ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+ #endif
+
+ virtio_console_send_ctrl(virtio_console_dev, &set_ctrl);
+
+ #ifdef RT_USING_SMP
+ level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+ #endif
+ }
+ break;
+ case VIRTIO_CONSOLE_PORT_NAME:
+ break;
+ default:
+ rt_kprintf("%s: Unsupport ctrl[id: %d, event: %d, value: %d]!\n",
+ dev_name, ctrl->id, ctrl->event, ctrl->value);
+ break;
+ }
+
+ } while (0);
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+
+ rt_list_for_each_entry(port_dev, &virtio_console_dev->port_head, node)
+ {
+ rt_uint32_t queue_rx_index = port_dev->queue_rx_index;
+ struct virtq *queue_rx = port_dev->queue_rx;
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&port_dev->spinlock_rx);
+#endif
+
+ if (queue_rx->used_idx != queue_rx->used->idx)
+ {
+ rt_hw_dsb();
+
+ id = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].id;
+ len = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].len;
+
+ if (port_dev->rx_notify)
+ {
+ #ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&port_dev->spinlock_rx, level);
+ #endif
+ /* Will call virtio_console_port_read to inc used_idx */
+
+ if (port_dev->parent.rx_indicate != RT_NULL)
+ {
+ port_dev->parent.rx_indicate(&port_dev->parent, len);
+ }
+
+ if (port_dev->rx_notify_helper.notify != RT_NULL)
+ {
+ port_dev->rx_notify_helper.notify(port_dev->rx_notify_helper.dev);
+ }
+
+ #ifdef RT_USING_SMP
+ level = rt_spin_lock_irqsave(&port_dev->spinlock_rx);
+ #endif
+ }
+ else
+ {
+ queue_rx->used_idx++;
+
+ virtio_submit_chain(virtio_dev, queue_rx_index, id);
+
+ virtio_queue_notify(virtio_dev, queue_rx_index);
+ }
+ }
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&port_dev->spinlock_rx, level);
+#endif
+ }
+}
+
+rt_err_t rt_virtio_console_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
+{
+ int i;
+ rt_size_t queues_num;
+ static int dev_no = 0;
+ char dev_name[RT_NAME_MAX];
+ struct virtio_device *virtio_dev;
+ struct virtio_console_device *virtio_console_dev;
+
+ RT_ASSERT(RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR > 0);
+
+ virtio_console_dev = rt_malloc(sizeof(struct virtio_console_device));
+
+ if (virtio_console_dev == RT_NULL)
+ {
+ goto _alloc_fail;
+ }
+
+ virtio_dev = &virtio_console_dev->virtio_dev;
+ virtio_dev->irq = irq;
+ virtio_dev->mmio_base = mmio_base;
+
+ virtio_console_dev->config = (struct virtio_console_config *)virtio_dev->mmio_config->config;
+
+#ifdef RT_USING_SMP
+ rt_spin_lock_init(&virtio_dev->spinlock);
+#endif
+
+ virtio_reset_device(virtio_dev);
+ virtio_status_acknowledge_driver(virtio_dev);
+
+ virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~(
+ (1 << VIRTIO_F_RING_EVENT_IDX) |
+ (1 << VIRTIO_F_RING_INDIRECT_DESC));
+
+ virtio_status_driver_ok(virtio_dev);
+
+ if (!virtio_has_feature(virtio_dev, VIRTIO_CONSOLE_F_MULTIPORT))
+ {
+ virtio_console_dev->max_port_nr = 1;
+ queues_num = 2;
+ }
+ else
+ {
+ if (virtio_console_dev->config->max_nr_ports > RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR)
+ {
+ virtio_console_dev->max_port_nr = RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR;
+ virtio_console_dev->config->max_nr_ports = virtio_console_dev->max_port_nr;
+ }
+ else
+ {
+ virtio_console_dev->max_port_nr = virtio_console_dev->config->max_nr_ports;
+ }
+
+ queues_num = VIRTIO_CONSOLE_PORT_QUEUE_INDEX(virtio_console_dev->max_port_nr, VIRTIO_CONSOLE_QUEUE_DATA_RX);
+ }
+
+ if (virtio_queues_alloc(virtio_dev, queues_num) != RT_EOK)
+ {
+ goto _alloc_fail;
+ }
+
+ for (i = 0; i < virtio_dev->queues_num; ++i)
+ {
+ if (virtio_queue_init(virtio_dev, i, VIRTIO_CONSOLE_QUEUE_SIZE) != RT_EOK)
+ {
+ for (; i >= 0; --i)
+ {
+ virtio_queue_destroy(virtio_dev, i);
+ }
+ goto _alloc_fail;
+ }
+ }
+
+ virtio_console_dev->parent.type = RT_Device_Class_Char;
+#ifdef RT_USING_DEVICE_OPS
+ virtio_console_dev->parent.ops = &virtio_console_ops;
+#else
+ virtio_console_dev->parent.init = virtio_console_init;
+ virtio_console_dev->parent.open = RT_NULL;
+ virtio_console_dev->parent.close = RT_NULL;
+ virtio_console_dev->parent.read = RT_NULL;
+ virtio_console_dev->parent.write = RT_NULL;
+ virtio_console_dev->parent.control = virtio_console_control;
+#endif
+
+ virtio_console_dev->parent.rx_indicate = RT_NULL;
+ virtio_console_dev->parent.tx_complete = RT_NULL;
+
+ virtio_console_dev->console_id = dev_no;
+ virtio_console_dev->port_nr = 0;
+ rt_list_init(&virtio_console_dev->port_head);
+
+ rt_snprintf(dev_name, RT_NAME_MAX, "virtio-console%d", dev_no++);
+
+ rt_hw_interrupt_install(irq, virtio_console_isr, virtio_console_dev, dev_name);
+ rt_hw_interrupt_umask(irq);
+
+ return rt_device_register((rt_device_t)virtio_console_dev, dev_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
+
+_alloc_fail:
+
+ if (virtio_console_dev != RT_NULL)
+ {
+ virtio_queues_free(virtio_dev);
+ rt_free(virtio_console_dev);
+ }
+ return -RT_ENOMEM;
+}
+#endif /* RT_USING_VIRTIO_CONSOLE */
diff --git a/components/drivers/virtio/virtio_console.h b/components/drivers/virtio/virtio_console.h
new file mode 100644
index 0000000000..f7d8d97e1d
--- /dev/null
+++ b/components/drivers/virtio/virtio_console.h
@@ -0,0 +1,97 @@
+/*
+ * 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 __VIRTIO_CONSOLE_H__
+#define __VIRTIO_CONSOLE_H__
+
+#include <rtdef.h>
+
+#include <virtio.h>
+
+#ifndef RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR
+#define RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR 4
+#endif
+
+#define VIRTIO_CONSOLE_QUEUE_DATA_RX 0
+#define VIRTIO_CONSOLE_QUEUE_DATA_TX 1
+#define VIRTIO_CONSOLE_QUEUE_CTRL_RX 2
+#define VIRTIO_CONSOLE_QUEUE_CTRL_TX 3
+#define VIRTIO_CONSOLE_QUEUE_SIZE 64
+
+/* Every port has data rx & tx, and port0 has ctrl rx & tx in multiport */
+#define VIRTIO_CONSOLE_PORT_QUEUE_INDEX(id, queue) ((id) * 2 + (!!(id)) * 2 + (queue))
+
+#define VIRTIO_CONSOLE_PORT_BAD_ID (~(rt_uint32_t)0)
+
+#define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */
+#define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */
+#define VIRTIO_CONSOLE_F_EMERG_WRITE 2 /* Does host support emergency write? */
+
+struct virtio_console_config
+{
+ rt_uint16_t cols;
+ rt_uint16_t rows;
+ rt_uint32_t max_nr_ports;
+ rt_uint32_t emerg_wr;
+} __attribute__((packed));
+
+struct virtio_console_control
+{
+ rt_uint32_t id; /* Port number */
+ rt_uint16_t event; /* The kind of control event */
+ rt_uint16_t value; /* Extra information for the event */
+};
+
+enum virtio_console_control_event
+{
+ VIRTIO_CONSOLE_DEVICE_READY = 0,
+ VIRTIO_CONSOLE_PORT_ADD,
+ VIRTIO_CONSOLE_PORT_REMOVE,
+ VIRTIO_CONSOLE_PORT_READY,
+ VIRTIO_CONSOLE_CONSOLE_PORT,
+ VIRTIO_CONSOLE_RESIZE,
+ VIRTIO_CONSOLE_PORT_OPEN,
+ VIRTIO_CONSOLE_PORT_NAME,
+};
+
+struct virtio_console_resize
+{
+ rt_uint16_t cols;
+ rt_uint16_t rows;
+};
+
+struct virtio_console_device
+{
+ struct rt_device parent;
+
+ struct virtio_device virtio_dev;
+
+ rt_uint32_t console_id;
+ rt_size_t port_nr;
+ rt_size_t max_port_nr;
+ rt_list_t port_head;
+ struct virtio_console_config *config;
+
+ struct
+ {
+ rt_ubase_t tx_ctrl_paddr;
+ struct virtio_console_control rx_ctrl, tx_ctrl;
+ } info[VIRTIO_CONSOLE_QUEUE_SIZE];
+};
+
+rt_err_t rt_virtio_console_init(rt_ubase_t *mmio_base, rt_uint32_t irq);
+
+enum
+{
+ VIRTIO_DEVICE_CTRL_CONSOLE_PORT_CREATE = 0x20,
+ VIRTIO_DEVICE_CTRL_CONSOLE_PORT_DESTROY,
+};
+
+#endif /* __VIRTIO_CONSOLE_H__ */
diff --git a/components/drivers/virtio/virtio_gpu.c b/components/drivers/virtio/virtio_gpu.c
new file mode 100644
index 0000000000..67ce28a876
--- /dev/null
+++ b/components/drivers/virtio/virtio_gpu.c
@@ -0,0 +1,934 @@
+/*
+ * 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 <rthw.h>
+#include <rtthread.h>
+#include <cpuport.h>
+
+#ifdef RT_USING_VIRTIO_GPU
+
+#include <virtio_gpu.h>
+
+static struct virtio_gpu_device *_primary_virtio_gpu_dev = RT_NULL;
+
+static rt_ubase_t _pixel_format_convert(rt_ubase_t format, rt_bool_t to_virtio_gpu_format)
+{
+ rt_ubase_t ret = 0;
+
+ if (to_virtio_gpu_format)
+ {
+ switch (format)
+ {
+ case RTGRAPHIC_PIXEL_FORMAT_RGB888:
+ ret = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM;
+ break;
+ case RTGRAPHIC_PIXEL_FORMAT_ARGB888:
+ ret = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM;
+ break;
+ case RTGRAPHIC_PIXEL_FORMAT_ABGR888:
+ ret = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch (format)
+ {
+ case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
+ ret = RTGRAPHIC_PIXEL_FORMAT_RGB888;
+ break;
+ case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
+ ret = RTGRAPHIC_PIXEL_FORMAT_ARGB888;
+ break;
+ case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
+ ret = RTGRAPHIC_PIXEL_FORMAT_ABGR888;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void virtio_gpu_ctrl_send_command(struct virtio_gpu_device *virtio_gpu_dev,
+ const void *cmd, rt_size_t cmd_len, void *res, rt_size_t res_len)
+{
+ rt_uint16_t idx[2];
+ void *addr = &virtio_gpu_dev->gpu_request;
+ void *ret_res = ((rt_uint8_t *)addr + cmd_len);
+ struct virtio_device *virtio_dev = &virtio_gpu_dev->virtio_dev;
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+
+ while (virtio_alloc_desc_chain(virtio_dev, VIRTIO_GPU_QUEUE_CTRL, 2, idx))
+ {
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+ rt_thread_yield();
+
+#ifdef RT_USING_SMP
+ level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+ }
+
+ rt_memcpy(&virtio_gpu_dev->gpu_request, cmd, cmd_len);
+
+ virtio_fill_desc(virtio_dev, VIRTIO_GPU_QUEUE_CTRL, idx[0],
+ VIRTIO_VA2PA(addr), cmd_len, VIRTQ_DESC_F_NEXT, idx[1]);
+
+ virtio_fill_desc(virtio_dev, VIRTIO_GPU_QUEUE_CTRL, idx[1],
+ VIRTIO_VA2PA(addr) + cmd_len, res_len, VIRTQ_DESC_F_WRITE, 0);
+
+ rt_memset(ret_res, 0, res_len);
+
+ virtio_gpu_dev->info[idx[0]].ctrl_valid = RT_TRUE;
+
+ virtio_submit_chain(virtio_dev, VIRTIO_GPU_QUEUE_CTRL, idx[0]);
+
+ virtio_queue_notify(virtio_dev, VIRTIO_GPU_QUEUE_CTRL);
+
+ while (virtio_gpu_dev->info[idx[0]].ctrl_valid)
+ {
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+ rt_thread_yield();
+
+#ifdef RT_USING_SMP
+ level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+ }
+
+ virtio_free_desc_chain(virtio_dev, VIRTIO_GPU_QUEUE_CTRL, idx[0]);
+
+ rt_memcpy(res, ret_res, res_len);
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+}
+
+static void virtio_gpu_cursor_send_command(struct virtio_gpu_device *virtio_gpu_dev,
+ const void *cmd, rt_size_t cmd_len)
+{
+ rt_uint16_t id;
+ void *addr;
+ struct virtio_device *virtio_dev = &virtio_gpu_dev->virtio_dev;
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+
+ while ((id = virtio_alloc_desc(virtio_dev, VIRTIO_GPU_QUEUE_CURSOR)) == VIRTQ_INVALID_DESC_ID)
+ {
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+ rt_thread_yield();
+
+#ifdef RT_USING_SMP
+ level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+ }
+
+ addr = &virtio_gpu_dev->info[id].cursor_cmd;
+ virtio_gpu_dev->info[id].cursor_valid = RT_TRUE;
+
+ rt_memcpy(addr, cmd, cmd_len);
+
+ virtio_fill_desc(virtio_dev, VIRTIO_GPU_QUEUE_CURSOR, id, VIRTIO_VA2PA(addr), cmd_len, 0, 0);
+
+ virtio_submit_chain(virtio_dev, VIRTIO_GPU_QUEUE_CURSOR, id);
+
+ virtio_queue_notify(virtio_dev, VIRTIO_GPU_QUEUE_CURSOR);
+
+ while (virtio_gpu_dev->info[id].cursor_valid)
+ {
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+ rt_thread_yield();
+
+#ifdef RT_USING_SMP
+ level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+ }
+
+ virtio_free_desc(virtio_dev, VIRTIO_GPU_QUEUE_CURSOR, id);
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+}
+
+static rt_err_t virtio_gpu_create_2d_resource(struct virtio_gpu_device *virtio_gpu_dev, enum virtio_gpu_formats format,
+ rt_uint32_t *resource_id, rt_uint32_t width, rt_uint32_t height)
+{
+ struct virtio_gpu_ctrl_hdr res;
+ struct virtio_gpu_resource_create_2d req;
+
+ *resource_id = ++virtio_gpu_dev->next_resource_id;
+
+ req.hdr.type = VIRTIO_GPU_CMD_RESOURCE_CREATE_2D;
+ req.resource_id = *resource_id;
+ req.format = format;
+ req.width = width;
+ req.height = height;
+
+ virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &res, sizeof(res));
+
+ if (res.type == VIRTIO_GPU_RESP_OK_NODATA)
+ {
+ return RT_EOK;
+ }
+
+ return -RT_ERROR;
+}
+
+static rt_err_t virtio_gpu_unref_resource(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t resource_id)
+{
+ struct virtio_gpu_ctrl_hdr res;
+ struct virtio_gpu_resource_unref req;
+
+ rt_memset(&req, 0, sizeof(req));
+
+ req.hdr.type = VIRTIO_GPU_CMD_RESOURCE_UNREF;
+ req.resource_id = resource_id;
+
+ virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &res, sizeof(res));
+
+ if (res.type == VIRTIO_GPU_RESP_OK_NODATA)
+ {
+ return RT_EOK;
+ }
+
+ return -RT_ERROR;
+}
+
+static rt_err_t virtio_gpu_attach_backing_resource(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t resource_id,
+ void *buffer, rt_size_t size)
+{
+ struct virtio_gpu_ctrl_hdr res;
+ struct
+ {
+ struct virtio_gpu_resource_attach_backing req;
+ struct virtio_gpu_mem_entry mem;
+ } req;
+
+ rt_memset(&req, 0, sizeof(req));
+
+ req.req.hdr.type = VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING;
+ req.req.resource_id = resource_id;
+ req.req.nr_entries = 1;
+
+ req.mem.addr = VIRTIO_VA2PA(buffer);
+ req.mem.length = size;
+
+ virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &res, sizeof(res));
+
+ if (res.type == VIRTIO_GPU_RESP_OK_NODATA)
+ {
+ return RT_EOK;
+ }
+
+ return -RT_ERROR;
+}
+
+static rt_err_t virtio_gpu_set_scanout(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t scanout_id,
+ rt_uint32_t resource_id, rt_uint32_t width, rt_uint32_t height)
+{
+ struct virtio_gpu_ctrl_hdr res;
+ struct virtio_gpu_set_scanout req;
+
+ rt_memset(&req, 0, sizeof(req));
+
+ req.hdr.type = VIRTIO_GPU_CMD_SET_SCANOUT;
+ req.r.x = 0;
+ req.r.y = 0;
+ req.r.width = width;
+ req.r.height = height;
+ req.scanout_id = scanout_id;
+ req.resource_id = resource_id;
+
+ virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &res, sizeof(res));
+
+ if (res.type == VIRTIO_GPU_RESP_OK_NODATA)
+ {
+ return RT_EOK;
+ }
+
+ return -RT_ERROR;
+}
+
+static rt_err_t virtio_gpu_flush_resource(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t resource_id,
+ rt_uint32_t x, rt_uint32_t y, rt_uint32_t width, rt_uint32_t height)
+{
+ struct virtio_gpu_ctrl_hdr res;
+ struct virtio_gpu_resource_flush req;
+
+ rt_memset(&req, 0, sizeof(req));
+
+ req.hdr.type = VIRTIO_GPU_CMD_RESOURCE_FLUSH;
+ req.r.x = x;
+ req.r.y = y;
+ req.r.width = width;
+ req.r.height = height;
+ req.resource_id = resource_id;
+
+ virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &res, sizeof(res));
+
+ if (res.type == VIRTIO_GPU_RESP_OK_NODATA)
+ {
+ return RT_EOK;
+ }
+
+ return -RT_ERROR;
+}
+
+static rt_err_t virtio_gpu_transfer_to_host_2d(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t resource_id,
+ rt_uint32_t x, rt_uint32_t y, rt_uint32_t width, rt_uint32_t height, rt_uint32_t offset)
+{
+ struct virtio_gpu_ctrl_hdr res;
+ struct virtio_gpu_transfer_to_host_2d req;
+
+ rt_memset(&req, 0, sizeof(req));
+
+ req.hdr.type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D;
+ req.r.x = x;
+ req.r.y = y;
+ req.r.width = width;
+ req.r.height = height;
+ req.offset = offset;
+ req.resource_id = resource_id;
+
+ virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &res, sizeof(res));
+
+ if (res.type == VIRTIO_GPU_RESP_OK_NODATA)
+ {
+ return RT_EOK;
+ }
+
+ return -RT_ERROR;
+}
+
+static rt_err_t virtio_gpu_gfx_flush_2d(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t resource_id,
+ rt_uint32_t x, rt_uint32_t y, rt_uint32_t width, rt_uint32_t height)
+{
+ rt_err_t status = virtio_gpu_transfer_to_host_2d(virtio_gpu_dev, resource_id, x, y, width, height, 0);
+
+ if (status == RT_EOK)
+ {
+ status = virtio_gpu_flush_resource(virtio_gpu_dev, resource_id, x, y, width, height);
+ }
+
+ return status;
+}
+
+static rt_err_t virtio_gpu_update_cursor(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t scanout_id,
+ rt_uint32_t resource_id, rt_uint32_t hot_x, rt_uint32_t hot_y)
+{
+ struct virtio_gpu_update_cursor req;
+
+ rt_memset(&req, 0, sizeof(req));
+
+ req.hdr.type = VIRTIO_GPU_CMD_UPDATE_CURSOR;
+ req.pos.scanout_id = scanout_id;
+ req.resource_id = resource_id;
+ req.hot_x = hot_x;
+ req.hot_y = hot_y;
+
+ virtio_gpu_cursor_send_command(virtio_gpu_dev, &req, sizeof(req));
+
+ return RT_EOK;
+}
+
+static rt_err_t virtio_gpu_cursor_move(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t scanout_id,
+ rt_uint32_t resource_id, rt_uint32_t x, rt_uint32_t y)
+{
+ struct virtio_gpu_update_cursor req;
+
+ rt_memset(&req, 0, sizeof(req));
+
+ req.hdr.type = VIRTIO_GPU_CMD_MOVE_CURSOR;
+ req.pos.scanout_id = scanout_id;
+ req.pos.x = x;
+ req.pos.y = y;
+ req.resource_id = resource_id;
+
+ virtio_gpu_cursor_send_command(virtio_gpu_dev, &req, sizeof(req));
+
+ return RT_EOK;
+}
+
+static rt_err_t virtio_gpu_cursor_set_img(struct virtio_gpu_device *virtio_gpu_dev, void *img)
+{
+ rt_err_t status;
+
+ rt_memcpy(virtio_gpu_dev->cursor_img, img, VIRTIO_GPU_CURSOR_IMG_SIZE);
+
+ status = virtio_gpu_attach_backing_resource(virtio_gpu_dev,
+ virtio_gpu_dev->cursor_resource_id, virtio_gpu_dev->cursor_img, VIRTIO_GPU_CURSOR_IMG_SIZE);
+
+ if (status != RT_EOK)
+ {
+ return status;
+ }
+
+ status = virtio_gpu_transfer_to_host_2d(virtio_gpu_dev, virtio_gpu_dev->cursor_resource_id,
+ 0, 0, VIRTIO_GPU_CURSOR_WIDTH, VIRTIO_GPU_CURSOR_HEIGHT, 0);
+
+ return status;
+}
+
+static rt_err_t virtio_gpu_get_display_info(struct virtio_gpu_device *virtio_gpu_dev)
+{
+ int i;
+ struct virtio_gpu_ctrl_hdr req;
+ struct virtio_gpu_resp_display_info info;
+
+ rt_memset(&req, 0, sizeof(req));
+ req.type = VIRTIO_GPU_CMD_GET_DISPLAY_INFO;
+
+ virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &info, sizeof(info));
+
+ if (info.hdr.type != VIRTIO_GPU_RESP_OK_DISPLAY_INFO)
+ {
+ return -RT_ERROR;
+ }
+
+ for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; ++i)
+ {
+ if (info.pmodes[i].enabled)
+ {
+ if (virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID)
+ {
+ rt_memcpy(&virtio_gpu_dev->pmode, &info.pmodes[i], sizeof(virtio_gpu_dev->pmode));
+ virtio_gpu_dev->pmode_id = i;
+ }
+ }
+ }
+
+ return RT_EOK;
+}
+
+static rt_err_t virtio_gpu_init(rt_device_t dev)
+{
+ rt_err_t status;
+ struct virtio_gpu_device *virtio_gpu_dev = (struct virtio_gpu_device *)dev;
+ struct virtio_device *virtio_dev = &virtio_gpu_dev->virtio_dev;
+ struct virtq *queue_ctrl, *queue_cursor;
+
+ queue_ctrl = &virtio_dev->queues[VIRTIO_GPU_QUEUE_CTRL];
+ queue_cursor = &virtio_dev->queues[VIRTIO_GPU_QUEUE_CURSOR];
+
+ queue_ctrl->avail->flags = 0;
+ queue_cursor->avail->flags = 0;
+
+ status = virtio_gpu_get_display_info(virtio_gpu_dev);
+
+ if (virtio_gpu_dev->pmode_id != VIRTIO_GPU_INVALID_PMODE_ID && _primary_virtio_gpu_dev == RT_NULL)
+ {
+ /* This device is ready */
+ _primary_virtio_gpu_dev = virtio_gpu_dev;
+ }
+
+ return status;
+}
+
+static rt_size_t virtio_gpu_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+ struct virtio_gpu_device *virtio_gpu_dev = (struct virtio_gpu_device *)dev;
+
+ if (virtio_gpu_dev->framebuffer == RT_NULL || pos + size >= virtio_gpu_dev->smem_len)
+ {
+ return 0;
+ }
+
+ rt_mutex_take(&virtio_gpu_dev->rw_mutex, RT_WAITING_FOREVER);
+
+ rt_memcpy(buffer, (rt_uint8_t *)virtio_gpu_dev->framebuffer + pos, size);
+
+ rt_mutex_release(&virtio_gpu_dev->rw_mutex);
+
+ return size;
+}
+
+static rt_size_t virtio_gpu_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+ struct virtio_gpu_device *virtio_gpu_dev = (struct virtio_gpu_device *)dev;
+
+ if (virtio_gpu_dev->framebuffer == RT_NULL || pos + size >= virtio_gpu_dev->smem_len)
+ {
+ return 0;
+ }
+
+ rt_mutex_take(&virtio_gpu_dev->rw_mutex, RT_WAITING_FOREVER);
+
+ rt_memcpy((rt_uint8_t *)virtio_gpu_dev->framebuffer + pos, buffer, size);
+
+ rt_mutex_release(&virtio_gpu_dev->rw_mutex);
+
+ return size;
+}
+
+static rt_err_t virtio_gpu_control(rt_device_t dev, int cmd, void *args)
+{
+ rt_err_t status = RT_EOK;
+ struct virtio_gpu_device *virtio_gpu_dev = (struct virtio_gpu_device *)dev;
+
+ switch (cmd)
+ {
+ case VIRTIO_DEVICE_CTRL_GPU_SET_PRIMARY:
+
+ _primary_virtio_gpu_dev = virtio_gpu_dev;
+
+ return status;
+ }
+
+ if (args == RT_NULL)
+ {
+ return -RT_ERROR;
+ }
+
+ switch (cmd)
+ {
+ case RTGRAPHIC_CTRL_RECT_UPDATE:
+ {
+ struct rt_device_rect_info *info = (struct rt_device_rect_info *)args;
+
+ if (virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID)
+ {
+ status = -RT_ERROR;
+ break;
+ }
+
+ status = virtio_gpu_gfx_flush_2d(virtio_gpu_dev, virtio_gpu_dev->display_resource_id,
+ info->x, info->y, info->width, info->height);
+ }
+ break;
+ case RTGRAPHIC_CTRL_GET_INFO:
+ {
+ struct rt_device_graphic_info *info = (struct rt_device_graphic_info *)args;
+
+ info->pixel_format = _pixel_format_convert((rt_ubase_t)args, RT_FALSE);
+ info->bits_per_pixel = VIRTIO_GPU_FORMAT_BPP;
+ info->pitch = virtio_gpu_dev->pmode.r.width * VIRTIO_GPU_FORMAT_PIXEL;
+ info->width = virtio_gpu_dev->pmode.r.width;
+ info->height = virtio_gpu_dev->pmode.r.height;
+ info->framebuffer = virtio_gpu_dev->framebuffer;
+ info->smem_len = virtio_gpu_dev->smem_len;
+ }
+ break;
+ case VIRTIO_DEVICE_CTRL_GPU_CREATE_2D:
+
+ virtio_gpu_dev->format = _pixel_format_convert((rt_ubase_t)args, RT_TRUE);
+
+ if (virtio_gpu_dev->format == 0 || virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID)
+ {
+ status = -RT_ERROR;
+ break;
+ }
+
+ status = virtio_gpu_create_2d_resource(virtio_gpu_dev, virtio_gpu_dev->format,
+ &virtio_gpu_dev->display_resource_id, virtio_gpu_dev->pmode.r.width, virtio_gpu_dev->pmode.r.height);
+
+ if (status != RT_EOK)
+ {
+ break;
+ }
+
+ virtio_gpu_dev->smem_len =
+ virtio_gpu_dev->pmode.r.width * virtio_gpu_dev->pmode.r.height * VIRTIO_GPU_FORMAT_PIXEL;
+ virtio_gpu_dev->smem_len = RT_ALIGN(virtio_gpu_dev->smem_len, VIRTIO_PAGE_SIZE);
+ virtio_gpu_dev->framebuffer = rt_malloc_align(virtio_gpu_dev->smem_len, VIRTIO_PAGE_SIZE);
+
+ if (virtio_gpu_dev->framebuffer == RT_NULL)
+ {
+ virtio_gpu_unref_resource(virtio_gpu_dev, virtio_gpu_dev->display_resource_id);
+
+ status = -RT_ENOMEM;
+ break;
+ }
+
+ status = virtio_gpu_attach_backing_resource(virtio_gpu_dev,
+ virtio_gpu_dev->display_resource_id, virtio_gpu_dev->framebuffer, virtio_gpu_dev->smem_len);
+
+ if (status != RT_EOK)
+ {
+ break;
+ }
+
+ status = virtio_gpu_set_scanout(virtio_gpu_dev, virtio_gpu_dev->pmode_id, virtio_gpu_dev->display_resource_id,
+ virtio_gpu_dev->pmode.r.width, virtio_gpu_dev->pmode.r.height);
+
+ break;
+ case VIRTIO_DEVICE_CTRL_CURSOR_SETUP:
+
+ if (virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID)
+ {
+ status = -RT_ERROR;
+ break;
+ }
+
+ rt_mutex_take(&virtio_gpu_dev->ops_mutex, RT_WAITING_FOREVER);
+
+ status = virtio_gpu_create_2d_resource(virtio_gpu_dev, virtio_gpu_dev->format,
+ &virtio_gpu_dev->cursor_resource_id, VIRTIO_GPU_CURSOR_WIDTH, VIRTIO_GPU_CURSOR_HEIGHT);
+
+ if (status != RT_EOK)
+ {
+ goto _cursor_setup_end;
+ }
+
+ status = virtio_gpu_cursor_set_img(virtio_gpu_dev, args);
+
+ if (status != RT_EOK)
+ {
+ goto _cursor_setup_end;
+ }
+
+ virtio_gpu_dev->cursor_x = 0;
+ virtio_gpu_dev->cursor_y = 0;
+
+ status = virtio_gpu_update_cursor(virtio_gpu_dev, virtio_gpu_dev->pmode_id, virtio_gpu_dev->cursor_resource_id,
+ virtio_gpu_dev->cursor_x, virtio_gpu_dev->cursor_y);
+
+ if (status == RT_EOK)
+ {
+ virtio_gpu_dev->cursor_enable = RT_TRUE;
+ }
+
+_cursor_setup_end:
+ rt_mutex_release(&virtio_gpu_dev->ops_mutex);
+
+ break;
+ case VIRTIO_DEVICE_CTRL_CURSOR_SET_IMG:
+
+ if (virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID || !virtio_gpu_dev->cursor_enable)
+ {
+ status = -RT_ERROR;
+ break;
+ }
+
+ rt_mutex_take(&virtio_gpu_dev->ops_mutex, RT_WAITING_FOREVER);
+
+ status = virtio_gpu_cursor_set_img(virtio_gpu_dev, args);
+
+ if (status != RT_EOK)
+ {
+ goto _cursor_set_img_end;
+ }
+
+ status = virtio_gpu_update_cursor(virtio_gpu_dev, virtio_gpu_dev->pmode_id, virtio_gpu_dev->cursor_resource_id,
+ virtio_gpu_dev->cursor_x, virtio_gpu_dev->cursor_y);
+
+_cursor_set_img_end:
+ rt_mutex_release(&virtio_gpu_dev->ops_mutex);
+
+ break;
+ case VIRTIO_DEVICE_CTRL_CURSOR_MOVE:
+
+ if (virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID || !virtio_gpu_dev->cursor_enable)
+ {
+ status = -RT_ERROR;
+ break;
+ }
+
+ rt_mutex_take(&virtio_gpu_dev->ops_mutex, RT_WAITING_FOREVER);
+
+ virtio_gpu_dev->cursor_x = ((rt_uint32_t *)args)[0];
+ virtio_gpu_dev->cursor_y = ((rt_uint32_t *)args)[1];
+
+ status = virtio_gpu_cursor_move(virtio_gpu_dev, virtio_gpu_dev->pmode_id, virtio_gpu_dev->cursor_resource_id,
+ virtio_gpu_dev->cursor_x, virtio_gpu_dev->cursor_y);
+
+ rt_mutex_release(&virtio_gpu_dev->ops_mutex);
+
+ break;
+ default:
+ status = -RT_EINVAL;
+ break;
+ }
+
+ return status;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops virtio_gpu_ops =
+{
+ virtio_gpu_init,
+ RT_NULL,
+ RT_NULL,
+ virtio_gpu_read,
+ virtio_gpu_write,
+ virtio_gpu_control
+};
+#endif
+
+static void virtio_gpu_set_pixel(const char *pixel, int x, int y)
+{
+ rt_uint8_t *fb;
+ struct virtio_gpu_device *virtio_gpu_dev = _primary_virtio_gpu_dev;
+
+ if (virtio_gpu_dev == RT_NULL || virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID)
+ {
+ return;
+ }
+
+ fb = (rt_uint8_t *)virtio_gpu_dev->framebuffer;
+ fb += (y * virtio_gpu_dev->pmode.r.width + x) * VIRTIO_GPU_FORMAT_PIXEL;
+ *((rt_uint32_t *)fb) = *((rt_uint32_t *)pixel);
+}
+
+static void virtio_gpu_get_pixel(char *pixel, int x, int y)
+{
+ rt_uint8_t *fb;
+ struct virtio_gpu_device *virtio_gpu_dev = _primary_virtio_gpu_dev;
+
+ if (virtio_gpu_dev == RT_NULL || virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID)
+ {
+ return;
+ }
+
+ fb = (rt_uint8_t *)virtio_gpu_dev->framebuffer;
+ *((rt_uint32_t *)pixel) = *(fb + (y * virtio_gpu_dev->pmode.r.width + x) * VIRTIO_GPU_FORMAT_PIXEL);
+}
+
+static void virtio_gpu_draw_hline(const char *pixel, int x1, int x2, int y)
+{
+ int i;
+ rt_uint8_t *fb;
+ rt_uint32_t color = *((rt_uint32_t *)pixel);
+ struct virtio_gpu_device *virtio_gpu_dev = _primary_virtio_gpu_dev;
+
+ if (virtio_gpu_dev == RT_NULL || virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID ||
+ x1 < 0 || x2 < 0 || y < 0)
+ {
+ return;
+ }
+
+ if (x1 > x2)
+ {
+ x1 ^= x2;
+ x2 ^= x1;
+ x1 ^= x2;
+ }
+
+ fb = (rt_uint8_t *)virtio_gpu_dev->framebuffer;
+ fb += (y * virtio_gpu_dev->pmode.r.width + x1) * VIRTIO_GPU_FORMAT_PIXEL;
+
+ for (i = x1; i < x2; ++i)
+ {
+ *((rt_uint32_t *)fb) = color;
+
+ fb += VIRTIO_GPU_FORMAT_PIXEL;
+ }
+}
+
+static void virtio_gpu_draw_vline(const char *pixel, int x, int y1, int y2)
+{
+ int i;
+ rt_uint8_t *fb;
+ rt_uint16_t pitch;
+ rt_uint32_t color = *((rt_uint32_t *)pixel);
+ struct virtio_gpu_device *virtio_gpu_dev = _primary_virtio_gpu_dev;
+
+ if (virtio_gpu_dev == RT_NULL || virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID ||
+ x < 0 || y1 < 0 || y2 < 0)
+ {
+ return;
+ }
+
+ if (y1 > y2)
+ {
+ y1 ^= y2;
+ y2 ^= y1;
+ y1 ^= y2;
+ }
+
+ fb = (rt_uint8_t *)virtio_gpu_dev->framebuffer;
+ fb += (y1 * virtio_gpu_dev->pmode.r.width + x) * VIRTIO_GPU_FORMAT_PIXEL;
+
+ pitch = virtio_gpu_dev->pmode.r.width * VIRTIO_GPU_FORMAT_PIXEL;
+
+ for (i = y1; i < y2; ++i)
+ {
+ *((rt_uint32_t *)fb) = color;
+
+ fb += pitch;
+ }
+}
+
+static void virtio_gpu_blit_line(const char *pixel, int x, int y, rt_size_t size)
+{
+ int i;
+ rt_uint8_t *fb;
+ rt_uint32_t *colors = (rt_uint32_t *)pixel;
+ struct virtio_gpu_device *virtio_gpu_dev = _primary_virtio_gpu_dev;
+
+ if (virtio_gpu_dev == RT_NULL || virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID || x < 0 || y < 0)
+ {
+ return;
+ }
+
+ fb = (rt_uint8_t *)virtio_gpu_dev->framebuffer;
+ fb += (y * virtio_gpu_dev->pmode.r.width + x) * VIRTIO_GPU_FORMAT_PIXEL;
+
+ for (i = 0; i < size; ++i)
+ {
+ *((rt_uint32_t *)fb) = *colors++;
+ fb += VIRTIO_GPU_FORMAT_PIXEL;
+ }
+}
+
+static struct rt_device_graphic_ops virtio_gpu_graphic_ops =
+{
+ virtio_gpu_set_pixel,
+ virtio_gpu_get_pixel,
+ virtio_gpu_draw_hline,
+ virtio_gpu_draw_vline,
+ virtio_gpu_blit_line
+};
+
+static void virtio_gpu_isr(int irqno, void *param)
+{
+ rt_uint16_t id;
+ struct virtio_gpu_device *virtio_gpu_dev = (struct virtio_gpu_device *)param;
+ struct virtio_device *virtio_dev = &virtio_gpu_dev->virtio_dev;
+ struct virtq *queue_ctrl = &virtio_dev->queues[VIRTIO_GPU_QUEUE_CTRL];
+ struct virtq *queue_cursor = &virtio_dev->queues[VIRTIO_GPU_QUEUE_CURSOR];
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+
+ virtio_interrupt_ack(virtio_dev);
+ rt_hw_dsb();
+
+ while (queue_ctrl->used_idx != queue_ctrl->used->idx)
+ {
+ rt_hw_dsb();
+ id = queue_ctrl->used->ring[queue_ctrl->used_idx % queue_ctrl->num].id;
+
+ virtio_gpu_dev->info[id].ctrl_valid = RT_FALSE;
+
+ queue_ctrl->used_idx++;
+ }
+
+ while (queue_cursor->used_idx != queue_cursor->used->idx)
+ {
+ rt_hw_dsb();
+ id = queue_cursor->used->ring[queue_cursor->used_idx % queue_cursor->num].id;
+
+ virtio_gpu_dev->info[id].cursor_valid = RT_FALSE;
+
+ queue_cursor->used_idx++;
+ }
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+}
+
+rt_err_t rt_virtio_gpu_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
+{
+ static int dev_no = 0;
+ char dev_name[RT_NAME_MAX];
+ struct virtio_device *virtio_dev;
+ struct virtio_gpu_device *virtio_gpu_dev;
+
+ virtio_gpu_dev = rt_malloc(sizeof(struct virtio_gpu_device));
+
+ if (virtio_gpu_dev == RT_NULL)
+ {
+ goto _alloc_fail;
+ }
+
+ virtio_dev = &virtio_gpu_dev->virtio_dev;
+ virtio_dev->irq = irq;
+ virtio_dev->mmio_base = mmio_base;
+
+ virtio_gpu_dev->pmode_id = VIRTIO_GPU_INVALID_PMODE_ID;
+ virtio_gpu_dev->display_resource_id = 0;
+ virtio_gpu_dev->cursor_resource_id = 0;
+ virtio_gpu_dev->next_resource_id = 0;
+ virtio_gpu_dev->framebuffer = RT_NULL;
+ virtio_gpu_dev->smem_len = 0;
+ virtio_gpu_dev->cursor_enable = RT_FALSE;
+
+#ifdef RT_USING_SMP
+ rt_spin_lock_init(&virtio_dev->spinlock);
+#endif
+
+ virtio_reset_device(virtio_dev);
+ virtio_status_acknowledge_driver(virtio_dev);
+
+ virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~(
+ (1 << VIRTIO_F_RING_EVENT_IDX) |
+ (1 << VIRTIO_F_RING_INDIRECT_DESC));
+
+ virtio_status_driver_ok(virtio_dev);
+
+ if (virtio_queues_alloc(virtio_dev, 2) != RT_EOK)
+ {
+ goto _alloc_fail;
+ }
+
+ if (virtio_queue_init(virtio_dev, VIRTIO_GPU_QUEUE_CTRL, VIRTIO_GPU_QUEUE_SIZE) != RT_EOK)
+ {
+ goto _alloc_fail;
+ }
+
+ if (virtio_queue_init(virtio_dev, VIRTIO_GPU_QUEUE_CURSOR, VIRTIO_GPU_QUEUE_SIZE) != RT_EOK)
+ {
+ virtio_queue_destroy(virtio_dev, VIRTIO_GPU_QUEUE_CTRL);
+
+ goto _alloc_fail;
+ }
+
+ virtio_gpu_dev->parent.type = RT_Device_Class_Graphic;
+#ifdef RT_USING_DEVICE_OPS
+ virtio_gpu_dev->parent.ops = &virtio_gpu_ops;
+#else
+ virtio_gpu_dev->parent.init = virtio_gpu_init;
+ virtio_gpu_dev->parent.open = RT_NULL;
+ virtio_gpu_dev->parent.close = RT_NULL;
+ virtio_gpu_dev->parent.read = virtio_gpu_read;
+ virtio_gpu_dev->parent.write = virtio_gpu_write;
+ virtio_gpu_dev->parent.control = virtio_gpu_control;
+#endif
+ virtio_gpu_dev->parent.user_data = &virtio_gpu_graphic_ops;
+
+ rt_snprintf(dev_name, RT_NAME_MAX, "virtio-gpu%d", dev_no++);
+
+ rt_mutex_init(&virtio_gpu_dev->rw_mutex, dev_name, RT_IPC_FLAG_PRIO);
+ rt_mutex_init(&virtio_gpu_dev->ops_mutex, dev_name, RT_IPC_FLAG_PRIO);
+
+ rt_hw_interrupt_install(irq, virtio_gpu_isr, virtio_gpu_dev, dev_name);
+ rt_hw_interrupt_umask(irq);
+
+ return rt_device_register((rt_device_t)virtio_gpu_dev, dev_name, RT_DEVICE_FLAG_RDWR);
+
+_alloc_fail:
+
+ if (virtio_gpu_dev != RT_NULL)
+ {
+ virtio_queues_free(virtio_dev);
+ rt_free(virtio_gpu_dev);
+ }
+ return -RT_ENOMEM;
+}
+#endif /* RT_USING_VIRTIO_GPU */
diff --git a/components/drivers/virtio/virtio_gpu.h b/components/drivers/virtio/virtio_gpu.h
new file mode 100644
index 0000000000..0c49ecbd2f
--- /dev/null
+++ b/components/drivers/virtio/virtio_gpu.h
@@ -0,0 +1,412 @@
+/*
+ * 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 __VIRTIO_GPU_H__
+#define __VIRTIO_GPU_H__
+
+#include <rtdef.h>
+
+#include <virtio.h>
+
+#define VIRTIO_GPU_QUEUE_CTRL 0
+#define VIRTIO_GPU_QUEUE_CURSOR 1
+#define VIRTIO_GPU_QUEUE_SIZE 32
+
+#define VIRTIO_GPU_F_VIRGL 0 /* VIRTIO_GPU_CMD_CTX_*, VIRTIO_GPU_CMD_*_3D */
+#define VIRTIO_GPU_F_EDID 1 /* VIRTIO_GPU_CMD_GET_EDID */
+#define VIRTIO_GPU_F_RESOURCE_UUID 2 /* VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID */
+#define VIRTIO_GPU_F_RESOURCE_BLOB 3 /* VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB */
+#define VIRTIO_GPU_F_CONTEXT_INIT 4 /* VIRTIO_GPU_CMD_CREATE_CONTEXT with context_init and multiple timelines */
+
+#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
+
+#define VIRTIO_GPU_FORMAT_BPP 32
+#define VIRTIO_GPU_FORMAT_PIXEL 4
+#define VIRTIO_GPU_CURSOR_WIDTH 64
+#define VIRTIO_GPU_CURSOR_HEIGHT 64
+#define VIRTIO_GPU_CURSOR_IMG_SIZE (VIRTIO_GPU_CURSOR_WIDTH * VIRTIO_GPU_CURSOR_HEIGHT * VIRTIO_GPU_FORMAT_PIXEL)
+#define VIRTIO_GPU_INVALID_PMODE_ID RT_UINT32_MAX
+
+/* GPU control */
+
+struct virtio_gpu_config
+{
+ rt_uint32_t events_read;
+ rt_uint32_t events_clear;
+ rt_uint32_t num_scanouts; /* 1 ~ 16 */
+ rt_uint32_t reserved;
+};
+
+enum virtio_gpu_ctrl_type
+{
+ VIRTIO_GPU_UNDEFINED = 0,
+
+ /* 2d commands */
+ VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
+ VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
+ VIRTIO_GPU_CMD_RESOURCE_UNREF,
+ VIRTIO_GPU_CMD_SET_SCANOUT,
+ VIRTIO_GPU_CMD_RESOURCE_FLUSH,
+ VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
+ VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
+ VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
+ VIRTIO_GPU_CMD_GET_CAPSET_INFO,
+ VIRTIO_GPU_CMD_GET_CAPSET,
+ VIRTIO_GPU_CMD_GET_EDID,
+ VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID,
+ VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB,
+ VIRTIO_GPU_CMD_SET_SCANOUT_BLOB,
+
+ /* 3d commands */
+ VIRTIO_GPU_CMD_CTX_CREATE = 0x0200,
+ VIRTIO_GPU_CMD_CTX_DESTROY,
+ VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE,
+ VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE,
+ VIRTIO_GPU_CMD_RESOURCE_CREATE_3D,
+ VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D,
+ VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D,
+ VIRTIO_GPU_CMD_SUBMIT_3D,
+ VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB,
+ VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB,
+
+ /* cursor commands */
+ VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
+ VIRTIO_GPU_CMD_MOVE_CURSOR,
+
+ /* success responses */
+ VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
+ VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
+ VIRTIO_GPU_RESP_OK_CAPSET_INFO,
+ VIRTIO_GPU_RESP_OK_CAPSET,
+ VIRTIO_GPU_RESP_OK_EDID,
+ VIRTIO_GPU_RESP_OK_RESOURCE_UUID,
+ VIRTIO_GPU_RESP_OK_MAP_INFO,
+
+ /* error responses */
+ VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
+ VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
+ VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
+ VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
+ VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
+ VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
+};
+
+#define VIRTIO_GPU_FLAG_FENCE (1 << 0)
+
+struct virtio_gpu_ctrl_hdr
+{
+ rt_uint32_t type;
+ rt_uint32_t flags;
+ rt_uint64_t fence_id;
+ rt_uint32_t ctx_id;
+ rt_uint8_t ring_idx;
+ rt_uint8_t padding[3];
+};
+
+#define VIRTIO_GPU_MAX_SCANOUTS 16
+
+struct virtio_gpu_rect
+{
+ rt_uint32_t x;
+ rt_uint32_t y;
+ rt_uint32_t width;
+ rt_uint32_t height;
+};
+
+struct virtio_gpu_resp_display_info
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_display_one
+ {
+ struct virtio_gpu_rect r;
+ rt_uint32_t enabled;
+ rt_uint32_t flags;
+ } pmodes[VIRTIO_GPU_MAX_SCANOUTS];
+};
+
+struct virtio_gpu_get_edid
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t scanout;
+ rt_uint32_t padding;
+};
+
+struct virtio_gpu_resp_edid
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t size;
+ rt_uint32_t padding;
+ rt_uint8_t edid[1024];
+};
+
+enum virtio_gpu_formats
+{
+ VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1,
+ VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM = 2,
+ VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM = 3,
+ VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM = 4,
+
+ VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67,
+ VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM = 68,
+
+ VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121,
+ VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134,
+};
+
+struct virtio_gpu_resource_create_2d
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t resource_id;
+ rt_uint32_t format;
+ rt_uint32_t width;
+ rt_uint32_t height;
+};
+
+struct virtio_gpu_resource_unref
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t resource_id;
+ rt_uint32_t padding;
+};
+
+struct virtio_gpu_set_scanout
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_rect r;
+ rt_uint32_t scanout_id;
+ rt_uint32_t resource_id;
+};
+
+struct virtio_gpu_resource_flush
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_rect r;
+ rt_uint32_t resource_id;
+ rt_uint32_t padding;
+};
+
+struct virtio_gpu_transfer_to_host_2d
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_rect r;
+ rt_uint64_t offset;
+ rt_uint32_t resource_id;
+ rt_uint32_t padding;
+};
+
+struct virtio_gpu_resource_attach_backing
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t resource_id;
+ rt_uint32_t nr_entries;
+};
+
+struct virtio_gpu_mem_entry
+{
+ rt_uint64_t addr;
+ rt_uint32_t length;
+ rt_uint32_t padding;
+};
+
+struct virtio_gpu_resource_detach_backing
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t resource_id;
+ rt_uint32_t padding;
+};
+
+struct virtio_gpu_get_capset_info
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t capset_index;
+ rt_uint32_t padding;
+};
+
+#define VIRTIO_GPU_CAPSET_VIRGL 1
+#define VIRTIO_GPU_CAPSET_VIRGL2 2
+#define VIRTIO_GPU_CAPSET_GFXSTREAM 3
+#define VIRTIO_GPU_CAPSET_VENUS 4
+#define VIRTIO_GPU_CAPSET_CROSS_DOMAIN 5
+
+struct virtio_gpu_resp_capset_info
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t capset_id;
+ rt_uint32_t capset_max_version;
+ rt_uint32_t capset_max_size;
+ rt_uint32_t padding;
+};
+
+struct virtio_gpu_get_capset
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t capset_id;
+ rt_uint32_t capset_version;
+};
+
+struct virtio_gpu_resp_capset
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint8_t capset_data[];
+};
+
+struct virtio_gpu_resource_assign_uuid
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t resource_id;
+ rt_uint32_t padding;
+};
+
+struct virtio_gpu_resp_resource_uuid
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint8_t uuid[16];
+};
+
+#define VIRTIO_GPU_BLOB_MEM_GUEST 0x0001
+#define VIRTIO_GPU_BLOB_MEM_HOST3D 0x0002
+#define VIRTIO_GPU_BLOB_MEM_HOST3D_GUEST 0x0003
+
+#define VIRTIO_GPU_BLOB_FLAG_USE_MAPPABLE 0x0001
+#define VIRTIO_GPU_BLOB_FLAG_USE_SHAREABLE 0x0002
+#define VIRTIO_GPU_BLOB_FLAG_USE_CROSS_DEVICE 0x0004
+
+struct virtio_gpu_resource_create_blob
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t resource_id;
+ rt_uint32_t blob_mem;
+ rt_uint32_t blob_flags;
+ rt_uint32_t nr_entries;
+ rt_uint64_t blob_id;
+ rt_uint64_t size;
+};
+
+struct virtio_gpu_set_scanout_blob
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_rect r;
+ rt_uint32_t scanout_id;
+ rt_uint32_t resource_id;
+ rt_uint32_t width;
+ rt_uint32_t height;
+ rt_uint32_t format;
+ rt_uint32_t padding;
+ rt_uint32_t strides[4];
+ rt_uint32_t offsets[4];
+};
+
+#define VIRTIO_GPU_CONTEXT_INIT_CAPSET_ID_MASK 0x000000ff
+struct virtio_gpu_ctx_create
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t nlen;
+ rt_uint32_t context_init;
+ char debug_name[64];
+};
+
+struct virtio_gpu_resource_map_blob
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t resource_id;
+ rt_uint32_t padding;
+ rt_uint64_t offset;
+};
+
+#define VIRTIO_GPU_MAP_CACHE_MASK 0x0f
+#define VIRTIO_GPU_MAP_CACHE_NONE 0x00
+#define VIRTIO_GPU_MAP_CACHE_CACHED 0x01
+#define VIRTIO_GPU_MAP_CACHE_UNCACHED 0x02
+#define VIRTIO_GPU_MAP_CACHE_WC 0x03
+
+struct virtio_gpu_resp_map_info
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t map_info;
+ rt_uint32_t padding;
+};
+
+struct virtio_gpu_resource_unmap_blob
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ rt_uint32_t resource_id;
+ rt_uint32_t padding;
+};
+
+/* GPU cursor */
+
+struct virtio_gpu_cursor_pos
+{
+ rt_uint32_t scanout_id;
+ rt_uint32_t x;
+ rt_uint32_t y;
+ rt_uint32_t padding;
+};
+
+struct virtio_gpu_update_cursor
+{
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_cursor_pos pos;
+ rt_uint32_t resource_id;
+ rt_uint32_t hot_x;
+ rt_uint32_t hot_y;
+ rt_uint32_t padding;
+};
+
+struct virtio_gpu_device
+{
+ struct rt_device parent;
+
+ struct virtio_device virtio_dev;
+
+ /* Current display's info */
+ struct virtio_gpu_display_one pmode;
+ enum virtio_gpu_formats format;
+ rt_uint32_t pmode_id;
+ rt_uint32_t cursor_x, cursor_y;
+ rt_uint32_t display_resource_id;
+ rt_uint32_t cursor_resource_id;
+ rt_uint32_t next_resource_id;
+
+ /* Display framebuffer */
+ struct rt_mutex rw_mutex;
+ void *framebuffer;
+ rt_uint32_t smem_len;
+
+ /* Cursor image info */
+ rt_bool_t cursor_enable;
+ struct rt_mutex ops_mutex;
+ rt_uint8_t cursor_img[VIRTIO_GPU_CURSOR_IMG_SIZE];
+
+ /* GPU request info */
+ struct virtio_gpu_resp_display_info gpu_request;
+
+ struct
+ {
+ rt_bool_t ctrl_valid;
+ rt_bool_t cursor_valid;
+
+ struct virtio_gpu_update_cursor cursor_cmd;
+ } info[VIRTIO_GPU_QUEUE_SIZE];
+};
+
+rt_err_t rt_virtio_gpu_init(rt_ubase_t *mmio_base, rt_uint32_t irq);
+
+enum
+{
+ VIRTIO_DEVICE_CTRL_GPU_SET_PRIMARY = 0x20,
+ VIRTIO_DEVICE_CTRL_GPU_CREATE_2D,
+
+ VIRTIO_DEVICE_CTRL_CURSOR_SETUP,
+ VIRTIO_DEVICE_CTRL_CURSOR_SET_IMG,
+ VIRTIO_DEVICE_CTRL_CURSOR_MOVE,
+};
+
+#endif /* __VIRTIO_GPU_H__ */
diff --git a/components/drivers/virtio/virtio_input.c b/components/drivers/virtio/virtio_input.c
new file mode 100644
index 0000000000..8de3d9c83f
--- /dev/null
+++ b/components/drivers/virtio/virtio_input.c
@@ -0,0 +1,449 @@
+/*
+ * 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 <rthw.h>
+#include <rtthread.h>
+#include <cpuport.h>
+
+#ifdef RT_USING_VIRTIO_INPUT
+
+#include <virtio_input.h>
+
+static void _set_bit(rt_uint32_t nr, volatile rt_ubase_t *addr)
+{
+ rt_ubase_t mask = BIT_MASK(nr);
+ rt_ubase_t *p = ((rt_ubase_t *)addr) + BIT_WORD(nr);
+
+ *p |= mask;
+}
+
+static rt_size_t virtio_input_cfg_select(struct virtio_input_device *virtio_input_dev,
+ rt_uint8_t select, rt_uint8_t subsel)
+{
+ struct virtio_input_config *config = virtio_input_dev->config;
+
+ rt_hw_dsb();
+ config->select = select;
+ config->subsel = subsel;
+ rt_hw_dsb();
+
+ return config->size;
+}
+
+static void virtio_input_cfg_bits(struct virtio_input_device *virtio_input_dev,
+ rt_uint8_t select, rt_uint8_t subsel, rt_ubase_t *bits, rt_uint32_t bitcount)
+{
+ int i;
+ rt_uint32_t bit;
+ rt_uint8_t bytes;
+ rt_uint8_t *virtio_bits;
+ void *config_base = virtio_input_dev->config;
+ rt_off_t offset = (rt_size_t)&((struct virtio_input_config *)0)->bitmap;
+
+ bytes = virtio_input_cfg_select(virtio_input_dev, select, subsel);
+
+ if (bytes == 0)
+ {
+ return;
+ }
+
+ if (bitcount > bytes * 8)
+ {
+ bitcount = bytes * 8;
+ }
+
+ /*
+ * Bitmap in virtio config space is a simple stream of bytes,
+ * with the first byte carrying bits 0-7, second bits 8-15 and
+ * so on.
+ */
+ virtio_bits = rt_malloc(bytes);
+
+ if (virtio_bits == RT_NULL)
+ {
+ return;
+ }
+
+ for (i = 0; i < bytes; ++i)
+ {
+ void *buffer = (void *)virtio_bits + i;
+
+ if (virtio_input_dev->virtio_dev.mmio_config->version == 1)
+ {
+ HWREG8(config_base + offset + i) = *((rt_uint8_t *)buffer);
+ }
+ else
+ {
+ rt_memcpy(config_base + offset + i, buffer, sizeof(rt_uint8_t));
+ }
+ }
+
+ for (bit = 0; bit < bitcount; ++bit)
+ {
+ if (virtio_bits[bit / 8] & (1 << (bit % 8)))
+ {
+ _set_bit(bit, bits);
+ }
+ }
+
+ rt_free(virtio_bits);
+
+ if (select == VIRTIO_INPUT_CFG_EV_BITS)
+ {
+ _set_bit(subsel, virtio_input_dev->ev_bit);
+ }
+}
+
+static rt_err_t virtio_input_init(rt_device_t dev)
+{
+ int i;
+ rt_uint16_t idx[VIRTIO_INPUT_QUEUE_MAX_SIZE];
+ struct virtio_input_device *virtio_input_dev = (struct virtio_input_device *)dev;
+ struct virtio_device *virtio_dev = &virtio_input_dev->virtio_dev;
+ struct virtq *queue_event, *queue_status;
+
+ virtio_input_cfg_bits(virtio_input_dev, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY, virtio_input_dev->key_bit, KEY_CNT);
+ virtio_input_cfg_bits(virtio_input_dev, VIRTIO_INPUT_CFG_EV_BITS, EV_REL, virtio_input_dev->rel_bit, REL_CNT);
+ virtio_input_cfg_bits(virtio_input_dev, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS, virtio_input_dev->abs_bit, ABS_CNT);
+
+ queue_event = &virtio_dev->queues[VIRTIO_INPUT_QUEUE_EVENT];
+ queue_status = &virtio_dev->queues[VIRTIO_INPUT_QUEUE_STATUS];
+
+ virtio_alloc_desc_chain(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT, queue_event->num, idx);
+ virtio_alloc_desc_chain(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS, queue_status->num, idx);
+
+ for (i = 0; i < queue_event->num; ++i)
+ {
+ rt_uint16_t id = i;
+ void *addr = &virtio_input_dev->recv_events[i];
+
+ virtio_fill_desc(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT, id,
+ VIRTIO_VA2PA(addr), sizeof(struct virtio_input_event), VIRTQ_DESC_F_WRITE, 0);
+
+ virtio_submit_chain(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT, id);
+ }
+ rt_hw_dsb();
+
+ queue_event->avail->flags = 0;
+ queue_status->avail->flags = VIRTQ_AVAIL_F_NO_INTERRUPT;
+
+ virtio_queue_notify(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT);
+
+ return RT_EOK;
+}
+
+static rt_size_t virtio_input_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+ struct virtio_input_device *virtio_input_dev = (struct virtio_input_device *)dev;
+
+ if (buffer == RT_NULL || pos + size >= virtio_input_dev->virtio_dev.queues[VIRTIO_INPUT_QUEUE_EVENT].num)
+ {
+ return 0;
+ }
+
+ rt_mutex_take(&virtio_input_dev->rw_mutex, RT_WAITING_FOREVER);
+
+ rt_memcpy(buffer, &virtio_input_dev->bcst_events[pos], size);
+
+ rt_mutex_release(&virtio_input_dev->rw_mutex);
+
+ return size;
+}
+
+static rt_size_t virtio_input_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+ struct virtio_input_device *virtio_input_dev = (struct virtio_input_device *)dev;
+
+ if (buffer == RT_NULL || pos + size >= virtio_input_dev->virtio_dev.queues[VIRTIO_INPUT_QUEUE_EVENT].num)
+ {
+ return 0;
+ }
+
+ rt_mutex_take(&virtio_input_dev->rw_mutex, RT_WAITING_FOREVER);
+
+ rt_memcpy(&virtio_input_dev->bcst_events[pos], buffer, size);
+
+ rt_mutex_release(&virtio_input_dev->rw_mutex);
+
+ return size;
+}
+
+static rt_err_t virtio_input_control(rt_device_t dev, int cmd, void *args)
+{
+ rt_err_t status = RT_EOK;
+ struct virtio_input_device *virtio_input_dev = (struct virtio_input_device *)dev;
+ struct virtio_device *virtio_dev = &virtio_input_dev->virtio_dev;
+ struct virtio_input_config *config = virtio_input_dev->config;
+
+ if (args == RT_NULL)
+ {
+ return -RT_ERROR;
+ }
+
+ switch (cmd)
+ {
+ case VIRTIO_DEVICE_CTRL_INPUT_GET_TYPE:
+
+ *(enum virtio_input_type *)args = virtio_input_dev->type;
+
+ break;
+ case VIRTIO_DEVICE_CTRL_INPUT_BIND_BSCT_HANDLER:
+
+ virtio_input_dev->bsct_handler = args;
+
+ break;
+ case VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_X_INFO:
+
+ virtio_input_cfg_select(virtio_input_dev, VIRTIO_INPUT_CFG_ABS_INFO, VIRTIO_INPUT_ABS_AXIS_X);
+ rt_memcpy(args, config, sizeof(struct virtio_input_config));
+
+ break;
+ case VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_Y_INFO:
+
+ virtio_input_cfg_select(virtio_input_dev, VIRTIO_INPUT_CFG_ABS_INFO, VIRTIO_INPUT_ABS_AXIS_Y);
+ rt_memcpy(args, config, sizeof(struct virtio_input_config));
+
+ break;
+ case VIRTIO_DEVICE_CTRL_INPUT_SET_STATUS:
+ {
+ rt_uint16_t id;
+ void *addr;
+ struct virtq *queue_status = &virtio_dev->queues[VIRTIO_INPUT_QUEUE_STATUS];
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+ id = queue_status->avail->idx % queue_status->num;
+ addr = &virtio_input_dev->xmit_events[id];
+
+ rt_memcpy(addr, args, sizeof(struct virtio_input_event));
+
+ virtio_free_desc(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS, id);
+
+ virtio_fill_desc(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS, id,
+ VIRTIO_VA2PA(addr), sizeof(struct virtio_input_event), 0, 0);
+
+ virtio_submit_chain(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS, id);
+
+ virtio_queue_notify(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS);
+
+ virtio_alloc_desc(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS);
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+ }
+ break;
+ case VIRTIO_DEVICE_CTRL_INPUT_GET_EV_BIT:
+
+ rt_memcpy(args, virtio_input_dev->ev_bit, sizeof(virtio_input_dev->ev_bit));
+
+ break;
+ case VIRTIO_DEVICE_CTRL_INPUT_GET_KEY_BIT:
+
+ rt_memcpy(args, virtio_input_dev->key_bit, sizeof(virtio_input_dev->key_bit));
+
+ break;
+ case VIRTIO_DEVICE_CTRL_INPUT_GET_REL_BIT:
+
+ rt_memcpy(args, virtio_input_dev->rel_bit, sizeof(virtio_input_dev->rel_bit));
+
+ break;
+ case VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_BIT:
+
+ rt_memcpy(args, virtio_input_dev->abs_bit, sizeof(virtio_input_dev->abs_bit));
+
+ break;
+ default:
+ status = -RT_EINVAL;
+ break;
+ }
+
+ return status;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops virtio_input_ops =
+{
+ virtio_input_init,
+ RT_NULL,
+ RT_NULL,
+ virtio_input_read,
+ virtio_input_write,
+ virtio_input_control
+};
+#endif
+
+static void virtio_input_isr(int irqno, void *param)
+{
+ struct virtio_input_device *virtio_input_dev = (struct virtio_input_device *)param;
+ struct virtio_device *virtio_dev = &virtio_input_dev->virtio_dev;
+ struct virtq *event_queue = &virtio_dev->queues[VIRTIO_INPUT_QUEUE_EVENT];
+ const char *dev_name = virtio_input_dev->parent.parent.name;
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+
+ virtio_interrupt_ack(virtio_dev);
+ rt_hw_dsb();
+
+ while (event_queue->used_idx != event_queue->used->idx)
+ {
+ rt_uint16_t id = event_queue->used->ring[event_queue->used_idx % event_queue->num].id;
+ rt_uint32_t len = event_queue->used->ring[event_queue->used_idx % event_queue->num].len;
+
+ if (len == sizeof(struct virtio_input_event))
+ {
+ struct virtio_input_event *recv_events = &virtio_input_dev->recv_events[id];
+ struct virtio_input_event *bcst_events = &virtio_input_dev->bcst_events[id];
+
+ if (recv_events->type >= EV_SYN && recv_events->type <= EV_ABS)
+ {
+ bcst_events->type = recv_events->type;
+ bcst_events->code = recv_events->code;
+ bcst_events->value = recv_events->value;
+
+ if (virtio_input_dev->bsct_handler != RT_NULL)
+ {
+ virtio_input_dev->bsct_handler(*bcst_events);
+ }
+ }
+ else
+ {
+ rt_kprintf("%s: Unsupport event[type: %02x, code: %02x, value: %08x]!\n",
+ dev_name, recv_events->type, recv_events->code, recv_events->value);
+ }
+ }
+ else
+ {
+ rt_kprintf("%s: Invalid event!\n", dev_name);
+ }
+
+ event_queue->used_idx++;
+
+ virtio_submit_chain(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT, id);
+
+ virtio_queue_notify(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT);
+ }
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+}
+
+rt_err_t rt_virtio_input_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
+{
+ rt_uint32_t flag;
+ static int dev_no = 0;
+ char dev_name[RT_NAME_MAX];
+ struct virtio_device *virtio_dev;
+ struct virtio_input_device *virtio_input_dev;
+
+ virtio_input_dev = rt_malloc(sizeof(struct virtio_input_device));
+
+ if (virtio_input_dev == RT_NULL)
+ {
+ goto _alloc_fail;
+ }
+
+ virtio_dev = &virtio_input_dev->virtio_dev;
+ virtio_dev->irq = irq;
+ virtio_dev->mmio_base = mmio_base;
+
+ virtio_input_dev->config = (struct virtio_input_config *)virtio_dev->mmio_config->config;
+ virtio_input_dev->bsct_handler = RT_NULL;
+
+#ifdef RT_USING_SMP
+ rt_spin_lock_init(&virtio_dev->spinlock);
+#endif
+
+ virtio_reset_device(virtio_dev);
+ virtio_status_acknowledge_driver(virtio_dev);
+
+ virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~(
+ (1 << VIRTIO_F_RING_EVENT_IDX) |
+ (1 << VIRTIO_F_RING_INDIRECT_DESC));
+
+ virtio_status_driver_ok(virtio_dev);
+
+ if (virtio_queues_alloc(virtio_dev, 2) != RT_EOK)
+ {
+ goto _alloc_fail;
+ }
+
+ if (virtio_queue_init(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT, VIRTIO_INPUT_EVENT_QUEUE_SIZE) != RT_EOK)
+ {
+ goto _alloc_fail;
+ }
+
+ if (virtio_queue_init(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS, VIRTIO_INPUT_STATUS_QUEUE_SIZE) != RT_EOK)
+ {
+ virtio_queue_destroy(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT);
+
+ goto _alloc_fail;
+ }
+
+ virtio_input_cfg_select(virtio_input_dev, VIRTIO_INPUT_CFG_ID_DEVIDS, 0);
+
+ if (virtio_input_dev->config->ids.product == EV_ABS)
+ {
+ virtio_input_dev->type = VIRTIO_INPUT_TYPE_TABLET;
+
+ virtio_input_dev->parent.type = RT_Device_Class_Touch;
+
+ flag = RT_DEVICE_FLAG_STANDALONE | RT_DEVICE_FLAG_INT_RX;
+ }
+ else
+ {
+ if (virtio_input_dev->config->ids.product == EV_KEY)
+ {
+ virtio_input_dev->type = VIRTIO_INPUT_TYPE_KEYBOARD;
+ }
+ else
+ {
+ virtio_input_dev->type = VIRTIO_INPUT_TYPE_MOUSE;
+ }
+
+ /* Replace it to "KeyBoard" or "Mouse" if support in the future */
+ virtio_input_dev->parent.type = RT_Device_Class_Miscellaneous;
+
+ flag = RT_DEVICE_FLAG_RDWR;
+ }
+#ifdef RT_USING_DEVICE_OPS
+ virtio_input_dev->parent.ops = &virtio_input_ops;
+#else
+ virtio_input_dev->parent.init = virtio_input_init;
+ virtio_input_dev->parent.open = RT_NULL;
+ virtio_input_dev->parent.close = RT_NULL;
+ virtio_input_dev->parent.read = virtio_input_read;
+ virtio_input_dev->parent.write = virtio_input_write;
+ virtio_input_dev->parent.control = virtio_input_control;
+#endif
+
+ rt_snprintf(dev_name, RT_NAME_MAX, "virtio-input%d", dev_no++);
+
+ rt_mutex_init(&virtio_input_dev->rw_mutex, dev_name, RT_IPC_FLAG_PRIO);
+
+ rt_hw_interrupt_install(irq, virtio_input_isr, virtio_input_dev, dev_name);
+ rt_hw_interrupt_umask(irq);
+
+ return rt_device_register((rt_device_t)virtio_input_dev, dev_name, flag);
+
+_alloc_fail:
+
+ if (virtio_input_dev != RT_NULL)
+ {
+ virtio_queues_free(virtio_dev);
+ rt_free(virtio_input_dev);
+ }
+ return -RT_ENOMEM;
+}
+#endif /* RT_USING_VIRTIO_INPUT */
diff --git a/components/drivers/virtio/virtio_input.h b/components/drivers/virtio/virtio_input.h
new file mode 100644
index 0000000000..367d0a5d6d
--- /dev/null
+++ b/components/drivers/virtio/virtio_input.h
@@ -0,0 +1,145 @@
+/*
+ * 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 __VIRTIO_INPUT_H__
+#define __VIRTIO_INPUT_H__
+
+#include <rtdef.h>
+
+#include <virtio.h>
+#include <virtio_input_event_codes.h>
+
+#define VIRTIO_INPUT_QUEUE_EVENT 0
+#define VIRTIO_INPUT_QUEUE_STATUS 1
+#define VIRTIO_INPUT_EVENT_QUEUE_SIZE 64
+#define VIRTIO_INPUT_STATUS_QUEUE_SIZE 8
+#define VIRTIO_INPUT_QUEUE_MAX_SIZE (VIRTIO_INPUT_EVENT_QUEUE_SIZE > VIRTIO_INPUT_STATUS_QUEUE_SIZE ? \
+ VIRTIO_INPUT_EVENT_QUEUE_SIZE : VIRTIO_INPUT_STATUS_QUEUE_SIZE)
+
+#define VIRTIO_INPUT_ABS_AXIS_X 0
+#define VIRTIO_INPUT_ABS_AXIS_Y 1
+
+enum virtio_input_type
+{
+ VIRTIO_INPUT_TYPE_KEYBOARD,
+ VIRTIO_INPUT_TYPE_MOUSE,
+ VIRTIO_INPUT_TYPE_TABLET,
+
+ VIRTIO_INPUT_TYPE_SIZE,
+};
+
+enum virtio_input_config_select
+{
+ VIRTIO_INPUT_CFG_UNSET = 0x00,
+ VIRTIO_INPUT_CFG_ID_NAME = 0x01,
+ VIRTIO_INPUT_CFG_ID_SERIAL = 0x02,
+ VIRTIO_INPUT_CFG_ID_DEVIDS = 0x03,
+ VIRTIO_INPUT_CFG_PROP_BITS = 0x10,
+ VIRTIO_INPUT_CFG_EV_BITS = 0x11,
+ VIRTIO_INPUT_CFG_ABS_INFO = 0x12,
+};
+
+struct virtio_input_absinfo
+{
+ rt_uint32_t min; /* Minimum value for the axis */
+ rt_uint32_t max; /* Maximum value for the axis */
+ rt_uint32_t fuzz; /* Fuzz value that is used to filter noise from the event stream */
+ rt_uint32_t flat; /* Within this value will be discarded by joydev interface and reported as 0 instead */
+ rt_uint32_t res; /* Resolution for the values reported for the axis */
+};
+
+struct virtio_input_devids
+{
+ rt_uint16_t bustype;
+ rt_uint16_t vendor;
+ rt_uint16_t product;
+ rt_uint16_t version;
+};
+
+struct virtio_input_config
+{
+ rt_uint8_t select;
+ rt_uint8_t subsel;
+ rt_uint8_t size;
+ rt_uint8_t reserved[5];
+
+ union
+ {
+ char string[128];
+ rt_uint8_t bitmap[128];
+ struct virtio_input_absinfo abs;
+ struct virtio_input_devids ids;
+ };
+} __attribute__((packed));
+
+struct virtio_input_event
+{
+ rt_uint16_t type;
+ rt_uint16_t code;
+ rt_uint32_t value;
+};
+
+#ifdef ARCH_CPU_64BIT
+#define BITS_PER_LONG 64
+#else
+#define BITS_PER_LONG 32
+#endif
+#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
+#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+
+#define BITS_PER_BYTE 8
+#define BITS_PER_TYPE(type) (sizeof(type) * BITS_PER_BYTE)
+#define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(char))
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(long))
+
+struct virtio_input_device
+{
+ struct rt_device parent;
+
+ struct virtio_device virtio_dev;
+
+ rt_ubase_t ev_bit[BITS_TO_LONGS(EV_CNT)];
+ rt_ubase_t key_bit[BITS_TO_LONGS(KEY_CNT)];
+ rt_ubase_t rel_bit[BITS_TO_LONGS(REL_CNT)];
+ rt_ubase_t abs_bit[BITS_TO_LONGS(ABS_CNT)];
+
+ enum virtio_input_type type;
+ struct virtio_input_config *config;
+
+ /* Broadcast events */
+ struct rt_mutex rw_mutex;
+ void (*bsct_handler)(struct virtio_input_event event);
+ struct virtio_input_event bcst_events[VIRTIO_INPUT_EVENT_QUEUE_SIZE];
+
+ /* Receive events */
+ struct virtio_input_event recv_events[VIRTIO_INPUT_EVENT_QUEUE_SIZE];
+
+ /* Transmit status */
+ struct virtio_input_event xmit_events[VIRTIO_INPUT_STATUS_QUEUE_SIZE];
+};
+
+enum
+{
+ VIRTIO_DEVICE_CTRL_INPUT_GET_TYPE = 0x20,
+ VIRTIO_DEVICE_CTRL_INPUT_BIND_BSCT_HANDLER,
+ VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_X_INFO,
+ VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_Y_INFO,
+ VIRTIO_DEVICE_CTRL_INPUT_SET_STATUS,
+
+ VIRTIO_DEVICE_CTRL_INPUT_GET_EV_BIT,
+ VIRTIO_DEVICE_CTRL_INPUT_GET_KEY_BIT,
+ VIRTIO_DEVICE_CTRL_INPUT_GET_REL_BIT,
+ VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_BIT,
+};
+
+rt_err_t rt_virtio_input_init(rt_ubase_t *mmio_base, rt_uint32_t irq);
+
+#endif /* __VIRTIO_INPUT_H__ */
diff --git a/components/drivers/virtio/virtio_input_event_codes.h b/components/drivers/virtio/virtio_input_event_codes.h
new file mode 100644
index 0000000000..b84723374e
--- /dev/null
+++ b/components/drivers/virtio/virtio_input_event_codes.h
@@ -0,0 +1,932 @@
+/*
+ * 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 __VIRTIO_INPUT_EVENT_CODES__
+#define __VIRTIO_INPUT_EVENT_CODES__
+
+/* Device properties and quirks */
+#define INPUT_PROP_POINTER 0x00 /* Needs a pointer */
+#define INPUT_PROP_DIRECT 0x01 /* Direct input devices */
+#define INPUT_PROP_BUTTONPAD 0x02 /* Has button(s) under pad */
+#define INPUT_PROP_SEMI_MT 0x03 /* Touch rectangle only */
+#define INPUT_PROP_TOPBUTTONPAD 0x04 /* Softbuttons at top of pad */
+#define INPUT_PROP_POINTING_STICK 0x05 /* Is a pointing stick */
+#define INPUT_PROP_ACCELEROMETER 0x06 /* Has accelerometer */
+
+#define INPUT_PROP_MAX 0x1f
+#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1)
+
+/* Event types */
+#define EV_SYN 0x00 /* Synchronization events */
+#define EV_KEY 0x01 /* Keys and buttons type */
+#define EV_REL 0x02 /* Relative axes events */
+#define EV_ABS 0x03 /* Absolute axes events */
+#define EV_MSC 0x04 /* Misc events */
+#define EV_SW 0x05 /* Switch events */
+#define EV_LED 0x11 /* LEDs events */
+#define EV_SND 0x12 /* Sounds events */
+#define EV_REP 0x14 /* Repeat events */
+#define EV_FF 0x15 /* Force feedback events */
+#define EV_PWR 0x16 /* Power management events */
+#define EV_FF_STATUS 0x17 /* Force feedback state */
+#define EV_MAX 0x1f /* Maximum number of events */
+#define EV_CNT (EV_MAX + 1)/* Event count */
+
+/* Synchronization events.*/
+#define SYN_REPORT 0
+#define SYN_CONFIG 1
+#define SYN_MT_REPORT 2
+#define SYN_DROPPED 3
+#define SYN_MAX 0xf
+#define SYN_CNT (SYN_MAX + 1)
+
+/*
+ * Keys and buttons
+ *
+ * Most of the keys/buttons are modeled after USB HUT 1.12
+ * (see http://www.usb.org/developers/hidpage).
+ * Abbreviations in the comments:
+ * AC - Application Control
+ * AL - Application Launch Button
+ * SC - System Control
+ */
+#define KEY_RESERVED 0
+#define KEY_ESC 1
+#define KEY_1 2
+#define KEY_2 3
+#define KEY_3 4
+#define KEY_4 5
+#define KEY_5 6
+#define KEY_6 7
+#define KEY_7 8
+#define KEY_8 9
+#define KEY_9 10
+#define KEY_0 11
+#define KEY_MINUS 12
+#define KEY_EQUAL 13
+#define KEY_BACKSPACE 14
+#define KEY_TAB 15
+#define KEY_Q 16
+#define KEY_W 17
+#define KEY_E 18
+#define KEY_R 19
+#define KEY_T 20
+#define KEY_Y 21
+#define KEY_U 22
+#define KEY_I 23
+#define KEY_O 24
+#define KEY_P 25
+#define KEY_LEFTBRACE 26
+#define KEY_RIGHTBRACE 27
+#define KEY_ENTER 28
+#define KEY_LEFTCTRL 29
+#define KEY_A 30
+#define KEY_S 31
+#define KEY_D 32
+#define KEY_F 33
+#define KEY_G 34
+#define KEY_H 35
+#define KEY_J 36
+#define KEY_K 37
+#define KEY_L 38
+#define KEY_SEMICOLON 39
+#define KEY_APOSTROPHE 40
+#define KEY_GRAVE 41
+#define KEY_LEFTSHIFT 42
+#define KEY_BACKSLASH 43
+#define KEY_Z 44
+#define KEY_X 45
+#define KEY_C 46
+#define KEY_V 47
+#define KEY_B 48
+#define KEY_N 49
+#define KEY_M 50
+#define KEY_COMMA 51
+#define KEY_DOT 52
+#define KEY_SLASH 53
+#define KEY_RIGHTSHIFT 54
+#define KEY_KPASTERISK 55
+#define KEY_LEFTALT 56
+#define KEY_SPACE 57
+#define KEY_CAPSLOCK 58
+#define KEY_F1 59
+#define KEY_F2 60
+#define KEY_F3 61
+#define KEY_F4 62
+#define KEY_F5 63
+#define KEY_F6 64
+#define KEY_F7 65
+#define KEY_F8 66
+#define KEY_F9 67
+#define KEY_F10 68
+#define KEY_NUMLOCK 69
+#define KEY_SCROLLLOCK 70
+#define KEY_KP7 71
+#define KEY_KP8 72
+#define KEY_KP9 73
+#define KEY_KPMINUS 74
+#define KEY_KP4 75
+#define KEY_KP5 76
+#define KEY_KP6 77
+#define KEY_KPPLUS 78
+#define KEY_KP1 79
+#define KEY_KP2 80
+#define KEY_KP3 81
+#define KEY_KP0 82
+#define KEY_KPDOT 83
+
+#define KEY_ZENKAKUHANKAKU 85
+#define KEY_102ND 86
+#define KEY_F11 87
+#define KEY_F12 88
+#define KEY_RO 89
+#define KEY_KATAKANA 90
+#define KEY_HIRAGANA 91
+#define KEY_HENKAN 92
+#define KEY_KATAKANAHIRAGANA 93
+#define KEY_MUHENKAN 94
+#define KEY_KPJPCOMMA 95
+#define KEY_KPENTER 96
+#define KEY_RIGHTCTRL 97
+#define KEY_KPSLASH 98
+#define KEY_SYSRQ 99
+#define KEY_RIGHTALT 100
+#define KEY_LINEFEED 101
+#define KEY_HOME 102
+#define KEY_UP 103
+#define KEY_PAGEUP 104
+#define KEY_LEFT 105
+#define KEY_RIGHT 106
+#define KEY_END 107
+#define KEY_DOWN 108
+#define KEY_PAGEDOWN 109
+#define KEY_INSERT 110
+#define KEY_DELETE 111
+#define KEY_MACRO 112
+#define KEY_MUTE 113
+#define KEY_VOLUMEDOWN 114
+#define KEY_VOLUMEUP 115
+#define KEY_POWER 116 /* SC System Power Down */
+#define KEY_KPEQUAL 117
+#define KEY_KPPLUSMINUS 118
+#define KEY_PAUSE 119
+#define KEY_SCALE 120 /* AL Compiz Scale (Expose) */
+
+#define KEY_KPCOMMA 121
+#define KEY_HANGEUL 122
+#define KEY_HANGUEL KEY_HANGEUL
+#define KEY_HANJA 123
+#define KEY_YEN 124
+#define KEY_LEFTMETA 125
+#define KEY_RIGHTMETA 126
+#define KEY_COMPOSE 127
+
+#define KEY_STOP 128 /* AC Stop */
+#define KEY_AGAIN 129
+#define KEY_PROPS 130 /* AC Properties */
+#define KEY_UNDO 131 /* AC Undo */
+#define KEY_FRONT 132
+#define KEY_COPY 133 /* AC Copy */
+#define KEY_OPEN 134 /* AC Open */
+#define KEY_PASTE 135 /* AC Paste */
+#define KEY_FIND 136 /* AC Search */
+#define KEY_CUT 137 /* AC Cut */
+#define KEY_HELP 138 /* AL Integrated Help Center */
+#define KEY_MENU 139 /* Menu (show menu) */
+#define KEY_CALC 140 /* AL Calculator */
+#define KEY_SETUP 141
+#define KEY_SLEEP 142 /* SC System Sleep */
+#define KEY_WAKEUP 143 /* System Wake Up */
+#define KEY_FILE 144 /* AL Local Machine Browser */
+#define KEY_SENDFILE 145
+#define KEY_DELETEFILE 146
+#define KEY_XFER 147
+#define KEY_PROG1 148
+#define KEY_PROG2 149
+#define KEY_WWW 150 /* AL Internet Browser */
+#define KEY_MSDOS 151
+#define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */
+#define KEY_SCREENLOCK KEY_COFFEE
+#define KEY_ROTATE_DISPLAY 153 /* Display orientation for e.g. tablets */
+#define KEY_DIRECTION KEY_ROTATE_DISPLAY
+#define KEY_CYCLEWINDOWS 154
+#define KEY_MAIL 155
+#define KEY_BOOKMARKS 156 /* AC Bookmarks */
+#define KEY_COMPUTER 157
+#define KEY_BACK 158 /* AC Back */
+#define KEY_FORWARD 159 /* AC Forward */
+#define KEY_CLOSECD 160
+#define KEY_EJECTCD 161
+#define KEY_EJECTCLOSECD 162
+#define KEY_NEXTSONG 163
+#define KEY_PLAYPAUSE 164
+#define KEY_PREVIOUSSONG 165
+#define KEY_STOPCD 166
+#define KEY_RECORD 167
+#define KEY_REWIND 168
+#define KEY_PHONE 169 /* Media Select Telephone */
+#define KEY_ISO 170
+#define KEY_CONFIG 171 /* AL Consumer Control Configuration */
+#define KEY_HOMEPAGE 172 /* AC Home */
+#define KEY_REFRESH 173 /* AC Refresh */
+#define KEY_EXIT 174 /* AC Exit */
+#define KEY_MOVE 175
+#define KEY_EDIT 176
+#define KEY_SCROLLUP 177
+#define KEY_SCROLLDOWN 178
+#define KEY_KPLEFTPAREN 179
+#define KEY_KPRIGHTPAREN 180
+#define KEY_NEW 181 /* AC New */
+#define KEY_REDO 182 /* AC Redo/Repeat */
+
+#define KEY_F13 183
+#define KEY_F14 184
+#define KEY_F15 185
+#define KEY_F16 186
+#define KEY_F17 187
+#define KEY_F18 188
+#define KEY_F19 189
+#define KEY_F20 190
+#define KEY_F21 191
+#define KEY_F22 192
+#define KEY_F23 193
+#define KEY_F24 194
+
+#define KEY_PLAYCD 200
+#define KEY_PAUSECD 201
+#define KEY_PROG3 202
+#define KEY_PROG4 203
+#define KEY_ALL_APPLICATIONS 204 /* AC Desktop Show All Applications */
+#define KEY_DASHBOARD KEY_ALL_APPLICATIONS
+#define KEY_SUSPEND 205
+#define KEY_CLOSE 206 /* AC Close */
+#define KEY_PLAY 207
+#define KEY_FASTFORWARD 208
+#define KEY_BASSBOOST 209
+#define KEY_PRINT 210 /* AC Print */
+#define KEY_HP 211
+#define KEY_CAMERA 212
+#define KEY_SOUND 213
+#define KEY_QUESTION 214
+#define KEY_EMAIL 215
+#define KEY_CHAT 216
+#define KEY_SEARCH 217
+#define KEY_CONNECT 218
+#define KEY_FINANCE 219 /* AL Checkbook/Finance */
+#define KEY_SPORT 220
+#define KEY_SHOP 221
+#define KEY_ALTERASE 222
+#define KEY_CANCEL 223 /* AC Cancel */
+#define KEY_BRIGHTNESSDOWN 224
+#define KEY_BRIGHTNESSUP 225
+#define KEY_MEDIA 226
+
+#define KEY_SWITCHVIDEOMODE 227 /* Cycle between available video outputs (Monitor/LCD/TV-out/etc) */
+#define KEY_KBDILLUMTOGGLE 228
+#define KEY_KBDILLUMDOWN 229
+#define KEY_KBDILLUMUP 230
+
+#define KEY_SEND 231 /* AC Send */
+#define KEY_REPLY 232 /* AC Reply */
+#define KEY_FORWARDMAIL 233 /* AC Forward Msg */
+#define KEY_SAVE 234 /* AC Save */
+#define KEY_DOCUMENTS 235
+
+#define KEY_BATTERY 236
+
+#define KEY_BLUETOOTH 237
+#define KEY_WLAN 238
+#define KEY_UWB 239
+
+#define KEY_UNKNOWN 240
+
+#define KEY_VIDEO_NEXT 241 /* Drive next video source */
+#define KEY_VIDEO_PREV 242 /* Drive previous video source */
+#define KEY_BRIGHTNESS_CYCLE 243 /* Brightness up, after max is min */
+#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual brightness control is off, rely on ambient */
+#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO
+#define KEY_DISPLAY_OFF 245 /* Display device to off state */
+
+#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */
+#define KEY_WIMAX KEY_WWAN
+#define KEY_RFKILL 247 /* Key that controls all radios */
+
+#define KEY_MICMUTE 248 /* Mute / unmute the microphone */
+
+/* Code 255 is reserved for special needs of AT keyboard driver */
+#define BTN_MISC 0x100
+#define BTN_0 0x100
+#define BTN_1 0x101
+#define BTN_2 0x102
+#define BTN_3 0x103
+#define BTN_4 0x104
+#define BTN_5 0x105
+#define BTN_6 0x106
+#define BTN_7 0x107
+#define BTN_8 0x108
+#define BTN_9 0x109
+
+#define BTN_MOUSE 0x110
+#define BTN_LEFT 0x110
+#define BTN_RIGHT 0x111
+#define BTN_MIDDLE 0x112
+#define BTN_SIDE 0x113
+#define BTN_EXTRA 0x114
+#define BTN_FORWARD 0x115
+#define BTN_BACK 0x116
+#define BTN_TASK 0x117
+
+#define BTN_JOYSTICK 0x120
+#define BTN_TRIGGER 0x120
+#define BTN_THUMB 0x121
+#define BTN_THUMB2 0x122
+#define BTN_TOP 0x123
+#define BTN_TOP2 0x124
+#define BTN_PINKIE 0x125
+#define BTN_BASE 0x126
+#define BTN_BASE2 0x127
+#define BTN_BASE3 0x128
+#define BTN_BASE4 0x129
+#define BTN_BASE5 0x12a
+#define BTN_BASE6 0x12b
+#define BTN_DEAD 0x12f
+
+#define BTN_GAMEPAD 0x130
+#define BTN_SOUTH 0x130
+#define BTN_A BTN_SOUTH
+#define BTN_EAST 0x131
+#define BTN_B BTN_EAST
+#define BTN_C 0x132
+#define BTN_NORTH 0x133
+#define BTN_X BTN_NORTH
+#define BTN_WEST 0x134
+#define BTN_Y BTN_WEST
+#define BTN_Z 0x135
+#define BTN_TL 0x136
+#define BTN_TR 0x137
+#define BTN_TL2 0x138
+#define BTN_TR2 0x139
+#define BTN_SELECT 0x13a
+#define BTN_START 0x13b
+#define BTN_MODE 0x13c
+#define BTN_THUMBL 0x13d
+#define BTN_THUMBR 0x13e
+
+#define BTN_DIGI 0x140
+#define BTN_TOOL_PEN 0x140
+#define BTN_TOOL_RUBBER 0x141
+#define BTN_TOOL_BRUSH 0x142
+#define BTN_TOOL_PENCIL 0x143
+#define BTN_TOOL_AIRBRUSH 0x144
+#define BTN_TOOL_FINGER 0x145
+#define BTN_TOOL_MOUSE 0x146
+#define BTN_TOOL_LENS 0x147
+#define BTN_TOOL_QUINTTAP 0x148 /* Five fingers on trackpad */
+#define BTN_STYLUS3 0x149
+#define BTN_TOUCH 0x14a
+#define BTN_STYLUS 0x14b
+#define BTN_STYLUS2 0x14c
+#define BTN_TOOL_DOUBLETAP 0x14d
+#define BTN_TOOL_TRIPLETAP 0x14e
+#define BTN_TOOL_QUADTAP 0x14f /* Four fingers on trackpad */
+
+#define BTN_WHEEL 0x150
+#define BTN_GEAR_DOWN 0x150
+#define BTN_GEAR_UP 0x151
+
+#define KEY_OK 0x160
+#define KEY_SELECT 0x161
+#define KEY_GOTO 0x162
+#define KEY_CLEAR 0x163
+#define KEY_POWER2 0x164
+#define KEY_OPTION 0x165
+#define KEY_INFO 0x166 /* AL OEM Features/Tips/Tutorial */
+#define KEY_TIME 0x167
+#define KEY_VENDOR 0x168
+#define KEY_ARCHIVE 0x169
+#define KEY_PROGRAM 0x16a /* Media Select Program Guide */
+#define KEY_CHANNEL 0x16b
+#define KEY_FAVORITES 0x16c
+#define KEY_EPG 0x16d
+#define KEY_PVR 0x16e /* Media Select Home */
+#define KEY_MHP 0x16f
+#define KEY_LANGUAGE 0x170
+#define KEY_TITLE 0x171
+#define KEY_SUBTITLE 0x172
+#define KEY_ANGLE 0x173
+#define KEY_FULL_SCREEN 0x174 /* AC View Toggle */
+#define KEY_ZOOM KEY_FULL_SCREEN
+#define KEY_MODE 0x175
+#define KEY_KEYBOARD 0x176
+#define KEY_ASPECT_RATIO 0x177 /* HUTRR37: Aspect */
+#define KEY_SCREEN KEY_ASPECT_RATIO
+#define KEY_PC 0x178 /* Media Select Computer */
+#define KEY_TV 0x179 /* Media Select TV */
+#define KEY_TV2 0x17a /* Media Select Cable */
+#define KEY_VCR 0x17b /* Media Select VCR */
+#define KEY_VCR2 0x17c /* VCR Plus */
+#define KEY_SAT 0x17d /* Media Select Satellite */
+#define KEY_SAT2 0x17e
+#define KEY_CD 0x17f /* Media Select CD */
+#define KEY_TAPE 0x180 /* Media Select Tape */
+#define KEY_RADIO 0x181
+#define KEY_TUNER 0x182 /* Media Select Tuner */
+#define KEY_PLAYER 0x183
+#define KEY_TEXT 0x184
+#define KEY_DVD 0x185 /* Media Select DVD */
+#define KEY_AUX 0x186
+#define KEY_MP3 0x187
+#define KEY_AUDIO 0x188 /* AL Audio Browser */
+#define KEY_VIDEO 0x189 /* AL Movie Browser */
+#define KEY_DIRECTORY 0x18a
+#define KEY_LIST 0x18b
+#define KEY_MEMO 0x18c /* Media Select Messages */
+#define KEY_CALENDAR 0x18d
+#define KEY_RED 0x18e
+#define KEY_GREEN 0x18f
+#define KEY_YELLOW 0x190
+#define KEY_BLUE 0x191
+#define KEY_CHANNELUP 0x192 /* Channel Increment */
+#define KEY_CHANNELDOWN 0x193 /* Channel Decrement */
+#define KEY_FIRST 0x194
+#define KEY_LAST 0x195 /* Recall Last */
+#define KEY_AB 0x196
+#define KEY_NEXT 0x197
+#define KEY_RESTART 0x198
+#define KEY_SLOW 0x199
+#define KEY_SHUFFLE 0x19a
+#define KEY_BREAK 0x19b
+#define KEY_PREVIOUS 0x19c
+#define KEY_DIGITS 0x19d
+#define KEY_TEEN 0x19e
+#define KEY_TWEN 0x19f
+#define KEY_VIDEOPHONE 0x1a0 /* Media Select Video Phone */
+#define KEY_GAMES 0x1a1 /* Media Select Games */
+#define KEY_ZOOMIN 0x1a2 /* AC Zoom In */
+#define KEY_ZOOMOUT 0x1a3 /* AC Zoom Out */
+#define KEY_ZOOMRESET 0x1a4 /* AC Zoom */
+#define KEY_WORDPROCESSOR 0x1a5 /* AL Word Processor */
+#define KEY_EDITOR 0x1a6 /* AL Text Editor */
+#define KEY_SPREADSHEET 0x1a7 /* AL Spreadsheet */
+#define KEY_GRAPHICSEDITOR 0x1a8 /* AL Graphics Editor */
+#define KEY_PRESENTATION 0x1a9 /* AL Presentation App */
+#define KEY_DATABASE 0x1aa /* AL Database App */
+#define KEY_NEWS 0x1ab /* AL Newsreader */
+#define KEY_VOICEMAIL 0x1ac /* AL Voicemail */
+#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */
+#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */
+#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */
+#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE
+#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */
+#define KEY_LOGOFF 0x1b1 /* AL Logoff */
+
+#define KEY_DOLLAR 0x1b2
+#define KEY_EURO 0x1b3
+
+#define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */
+#define KEY_FRAMEFORWARD 0x1b5
+#define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */
+#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */
+#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */
+#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */
+#define KEY_IMAGES 0x1ba /* AL Image Browser */
+#define KEY_NOTIFICATION_CENTER 0x1bc /* Show/hide the notification center */
+#define KEY_PICKUP_PHONE 0x1bd /* Answer incoming call */
+#define KEY_HANGUP_PHONE 0x1be /* Decline incoming call */
+
+#define KEY_DEL_EOL 0x1c0
+#define KEY_DEL_EOS 0x1c1
+#define KEY_INS_LINE 0x1c2
+#define KEY_DEL_LINE 0x1c3
+
+#define KEY_FN 0x1d0
+#define KEY_FN_ESC 0x1d1
+#define KEY_FN_F1 0x1d2
+#define KEY_FN_F2 0x1d3
+#define KEY_FN_F3 0x1d4
+#define KEY_FN_F4 0x1d5
+#define KEY_FN_F5 0x1d6
+#define KEY_FN_F6 0x1d7
+#define KEY_FN_F7 0x1d8
+#define KEY_FN_F8 0x1d9
+#define KEY_FN_F9 0x1da
+#define KEY_FN_F10 0x1db
+#define KEY_FN_F11 0x1dc
+#define KEY_FN_F12 0x1dd
+#define KEY_FN_1 0x1de
+#define KEY_FN_2 0x1df
+#define KEY_FN_D 0x1e0
+#define KEY_FN_E 0x1e1
+#define KEY_FN_F 0x1e2
+#define KEY_FN_S 0x1e3
+#define KEY_FN_B 0x1e4
+#define KEY_FN_RIGHT_SHIFT 0x1e5
+
+#define KEY_BRL_DOT1 0x1f1
+#define KEY_BRL_DOT2 0x1f2
+#define KEY_BRL_DOT3 0x1f3
+#define KEY_BRL_DOT4 0x1f4
+#define KEY_BRL_DOT5 0x1f5
+#define KEY_BRL_DOT6 0x1f6
+#define KEY_BRL_DOT7 0x1f7
+#define KEY_BRL_DOT8 0x1f8
+#define KEY_BRL_DOT9 0x1f9
+#define KEY_BRL_DOT10 0x1fa
+
+#define KEY_NUMERIC_0 0x200 /* Used by phones, remote controls, and other keypads */
+#define KEY_NUMERIC_1 0x201
+#define KEY_NUMERIC_2 0x202
+#define KEY_NUMERIC_3 0x203
+#define KEY_NUMERIC_4 0x204
+#define KEY_NUMERIC_5 0x205
+#define KEY_NUMERIC_6 0x206
+#define KEY_NUMERIC_7 0x207
+#define KEY_NUMERIC_8 0x208
+#define KEY_NUMERIC_9 0x209
+#define KEY_NUMERIC_STAR 0x20a
+#define KEY_NUMERIC_POUND 0x20b
+#define KEY_NUMERIC_A 0x20c /* Phone key A - HUT Telephony 0xb9 */
+#define KEY_NUMERIC_B 0x20d
+#define KEY_NUMERIC_C 0x20e
+#define KEY_NUMERIC_D 0x20f
+
+#define KEY_CAMERA_FOCUS 0x210
+#define KEY_WPS_BUTTON 0x211 /* WiFi Protected Setup key */
+
+#define KEY_TOUCHPAD_TOGGLE 0x212 /* Request switch touchpad on or off */
+#define KEY_TOUCHPAD_ON 0x213
+#define KEY_TOUCHPAD_OFF 0x214
+
+#define KEY_CAMERA_ZOOMIN 0x215
+#define KEY_CAMERA_ZOOMOUT 0x216
+#define KEY_CAMERA_UP 0x217
+#define KEY_CAMERA_DOWN 0x218
+#define KEY_CAMERA_LEFT 0x219
+#define KEY_CAMERA_RIGHT 0x21a
+
+#define KEY_ATTENDANT_ON 0x21b
+#define KEY_ATTENDANT_OFF 0x21c
+#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */
+#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */
+
+#define BTN_DPAD_UP 0x220
+#define BTN_DPAD_DOWN 0x221
+#define BTN_DPAD_LEFT 0x222
+#define BTN_DPAD_RIGHT 0x223
+
+#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */
+#define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */
+
+#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */
+#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */
+#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */
+#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */
+#define KEY_APPSELECT 0x244 /* AL Select Task/Application */
+#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */
+#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */
+#define KEY_ASSISTANT 0x247 /* AL Context-aware desktop assistant */
+#define KEY_KBD_LAYOUT_NEXT 0x248 /* AC Next Keyboard Layout Select */
+#define KEY_EMOJI_PICKER 0x249 /* Show/hide emoji picker (HUTRR101) */
+#define KEY_DICTATE 0x24a /* Start or Stop Voice Dictation Session (HUTRR99) */
+
+#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */
+#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */
+
+#define KEY_KBDINPUTASSIST_PREV 0x260
+#define KEY_KBDINPUTASSIST_NEXT 0x261
+#define KEY_KBDINPUTASSIST_PREVGROUP 0x262
+#define KEY_KBDINPUTASSIST_NEXTGROUP 0x263
+#define KEY_KBDINPUTASSIST_ACCEPT 0x264
+#define KEY_KBDINPUTASSIST_CANCEL 0x265
+
+/* Diagonal movement keys */
+#define KEY_RIGHT_UP 0x266
+#define KEY_RIGHT_DOWN 0x267
+#define KEY_LEFT_UP 0x268
+#define KEY_LEFT_DOWN 0x269
+
+#define KEY_ROOT_MENU 0x26a /* Show Device's Root Menu */
+/* Show Top Menu of the Media (e.g. DVD) */
+#define KEY_MEDIA_TOP_MENU 0x26b
+#define KEY_NUMERIC_11 0x26c
+#define KEY_NUMERIC_12 0x26d
+/*
+ * Toggle Audio Description: refers to an audio service that helps blind and
+ * visually impaired consumers understand the action in a program. Note: in
+ * some countries this is referred to as "Video Description".
+ */
+#define KEY_AUDIO_DESC 0x26e
+#define KEY_3D_MODE 0x26f
+#define KEY_NEXT_FAVORITE 0x270
+#define KEY_STOP_RECORD 0x271
+#define KEY_PAUSE_RECORD 0x272
+#define KEY_VOD 0x273 /* Video on Demand */
+#define KEY_UNMUTE 0x274
+#define KEY_FASTREVERSE 0x275
+#define KEY_SLOWREVERSE 0x276
+/*
+ * Control a data application associated with the currently viewed channel,
+ * e.g. teletext or data broadcast application (MHEG, MHP, HbbTV, etc.)
+ */
+#define KEY_DATA 0x277
+#define KEY_ONSCREEN_KEYBOARD 0x278
+/* Electronic privacy screen control */
+#define KEY_PRIVACY_SCREEN_TOGGLE 0x279
+
+/* Select an area of screen to be copied */
+#define KEY_SELECTIVE_SCREENSHOT 0x27a
+
+/* Move the focus to the next or previous user controllable element within a UI container */
+#define KEY_NEXT_ELEMENT 0x27b
+#define KEY_PREVIOUS_ELEMENT 0x27c
+
+/* Toggle Autopilot engagement */
+#define KEY_AUTOPILOT_ENGAGE_TOGGLE 0x27d
+
+/* Shortcut Keys */
+#define KEY_MARK_WAYPOINT 0x27e
+#define KEY_SOS 0x27f
+#define KEY_NAV_CHART 0x280
+#define KEY_FISHING_CHART 0x281
+#define KEY_SINGLE_RANGE_RADAR 0x282
+#define KEY_DUAL_RANGE_RADAR 0x283
+#define KEY_RADAR_OVERLAY 0x284
+#define KEY_TRADITIONAL_SONAR 0x285
+#define KEY_CLEARVU_SONAR 0x286
+#define KEY_SIDEVU_SONAR 0x287
+#define KEY_NAV_INFO 0x288
+#define KEY_BRIGHTNESS_MENU 0x289
+
+/*
+ * Some keyboards have keys which do not have a defined meaning, these keys
+ * are intended to be programmed / bound to macros by the user. For most
+ * keyboards with these macro-keys the key-sequence to inject, or action to
+ * take, is all handled by software on the host side. So from the kernel's
+ * point of view these are just normal keys.
+ *
+ * The KEY_MACRO# codes below are intended for such keys, which may be labeled
+ * e.g. G1-G18, or S1 - S30. The KEY_MACRO# codes MUST NOT be used for keys
+ * where the marking on the key does indicate a defined meaning / purpose.
+ *
+ * The KEY_MACRO# codes MUST also NOT be used as fallback for when no existing
+ * KEY_FOO define matches the marking / purpose. In this case a new KEY_FOO
+ * define MUST be added.
+ */
+#define KEY_MACRO1 0x290
+#define KEY_MACRO2 0x291
+#define KEY_MACRO3 0x292
+#define KEY_MACRO4 0x293
+#define KEY_MACRO5 0x294
+#define KEY_MACRO6 0x295
+#define KEY_MACRO7 0x296
+#define KEY_MACRO8 0x297
+#define KEY_MACRO9 0x298
+#define KEY_MACRO10 0x299
+#define KEY_MACRO11 0x29a
+#define KEY_MACRO12 0x29b
+#define KEY_MACRO13 0x29c
+#define KEY_MACRO14 0x29d
+#define KEY_MACRO15 0x29e
+#define KEY_MACRO16 0x29f
+#define KEY_MACRO17 0x2a0
+#define KEY_MACRO18 0x2a1
+#define KEY_MACRO19 0x2a2
+#define KEY_MACRO20 0x2a3
+#define KEY_MACRO21 0x2a4
+#define KEY_MACRO22 0x2a5
+#define KEY_MACRO23 0x2a6
+#define KEY_MACRO24 0x2a7
+#define KEY_MACRO25 0x2a8
+#define KEY_MACRO26 0x2a9
+#define KEY_MACRO27 0x2aa
+#define KEY_MACRO28 0x2ab
+#define KEY_MACRO29 0x2ac
+#define KEY_MACRO30 0x2ad
+
+/*
+ * Some keyboards with the macro-keys described above have some extra keys
+ * for controlling the host-side software responsible for the macro handling:
+ * -A macro recording start/stop key. Note that not all keyboards which emit
+ * KEY_MACRO_RECORD_START will also emit KEY_MACRO_RECORD_STOP if
+ * KEY_MACRO_RECORD_STOP is not advertised, then KEY_MACRO_RECORD_START
+ * should be interpreted as a recording start/stop toggle;
+ * -Keys for switching between different macro (pre)sets, either a key for
+ * cycling through the configured presets or keys to directly select a preset.
+ */
+#define KEY_MACRO_RECORD_START 0x2b0
+#define KEY_MACRO_RECORD_STOP 0x2b1
+#define KEY_MACRO_PRESET_CYCLE 0x2b2
+#define KEY_MACRO_PRESET1 0x2b3
+#define KEY_MACRO_PRESET2 0x2b4
+#define KEY_MACRO_PRESET3 0x2b5
+
+/*
+ * Some keyboards have a buildin LCD panel where the contents are controlled
+ * by the host. Often these have a number of keys directly below the LCD
+ * intended for controlling a menu shown on the LCD. These keys often don't
+ * have any labeling so we just name them KEY_KBD_LCD_MENU#
+ */
+#define KEY_KBD_LCD_MENU1 0x2b8
+#define KEY_KBD_LCD_MENU2 0x2b9
+#define KEY_KBD_LCD_MENU3 0x2ba
+#define KEY_KBD_LCD_MENU4 0x2bb
+#define KEY_KBD_LCD_MENU5 0x2bc
+
+#define BTN_TRIGGER_HAPPY 0x2c0
+#define BTN_TRIGGER_HAPPY1 0x2c0
+#define BTN_TRIGGER_HAPPY2 0x2c1
+#define BTN_TRIGGER_HAPPY3 0x2c2
+#define BTN_TRIGGER_HAPPY4 0x2c3
+#define BTN_TRIGGER_HAPPY5 0x2c4
+#define BTN_TRIGGER_HAPPY6 0x2c5
+#define BTN_TRIGGER_HAPPY7 0x2c6
+#define BTN_TRIGGER_HAPPY8 0x2c7
+#define BTN_TRIGGER_HAPPY9 0x2c8
+#define BTN_TRIGGER_HAPPY10 0x2c9
+#define BTN_TRIGGER_HAPPY11 0x2ca
+#define BTN_TRIGGER_HAPPY12 0x2cb
+#define BTN_TRIGGER_HAPPY13 0x2cc
+#define BTN_TRIGGER_HAPPY14 0x2cd
+#define BTN_TRIGGER_HAPPY15 0x2ce
+#define BTN_TRIGGER_HAPPY16 0x2cf
+#define BTN_TRIGGER_HAPPY17 0x2d0
+#define BTN_TRIGGER_HAPPY18 0x2d1
+#define BTN_TRIGGER_HAPPY19 0x2d2
+#define BTN_TRIGGER_HAPPY20 0x2d3
+#define BTN_TRIGGER_HAPPY21 0x2d4
+#define BTN_TRIGGER_HAPPY22 0x2d5
+#define BTN_TRIGGER_HAPPY23 0x2d6
+#define BTN_TRIGGER_HAPPY24 0x2d7
+#define BTN_TRIGGER_HAPPY25 0x2d8
+#define BTN_TRIGGER_HAPPY26 0x2d9
+#define BTN_TRIGGER_HAPPY27 0x2da
+#define BTN_TRIGGER_HAPPY28 0x2db
+#define BTN_TRIGGER_HAPPY29 0x2dc
+#define BTN_TRIGGER_HAPPY30 0x2dd
+#define BTN_TRIGGER_HAPPY31 0x2de
+#define BTN_TRIGGER_HAPPY32 0x2df
+#define BTN_TRIGGER_HAPPY33 0x2e0
+#define BTN_TRIGGER_HAPPY34 0x2e1
+#define BTN_TRIGGER_HAPPY35 0x2e2
+#define BTN_TRIGGER_HAPPY36 0x2e3
+#define BTN_TRIGGER_HAPPY37 0x2e4
+#define BTN_TRIGGER_HAPPY38 0x2e5
+#define BTN_TRIGGER_HAPPY39 0x2e6
+#define BTN_TRIGGER_HAPPY40 0x2e7
+
+/* We avoid low common keys in module aliases so they don't get huge. */
+#define KEY_MIN_INTERESTING KEY_MUTE
+#define KEY_MAX 0x2ff
+#define KEY_CNT (KEY_MAX + 1)
+
+/* Relative axes */
+#define REL_X 0x00
+#define REL_Y 0x01
+#define REL_Z 0x02
+#define REL_RX 0x03
+#define REL_RY 0x04
+#define REL_RZ 0x05
+#define REL_HWHEEL 0x06
+#define REL_DIAL 0x07
+#define REL_WHEEL 0x08
+#define REL_MISC 0x09
+/*
+ * 0x0a is reserved and should not be used in input drivers.
+ * It was used by HID as REL_MISC + 1 and userspace needs to detect if
+ * the next REL_* event is correct or is just REL_MISC + n.
+ * We define here REL_RESERVED so userspace can rely on it and detect
+ * the situation described above.
+ */
+#define REL_RESERVED 0x0a
+#define REL_WHEEL_HI_RES 0x0b
+#define REL_HWHEEL_HI_RES 0x0c
+#define REL_MAX 0x0f
+#define REL_CNT (REL_MAX + 1)
+
+/* Absolute axes */
+#define ABS_X 0x00
+#define ABS_Y 0x01
+#define ABS_Z 0x02
+#define ABS_RX 0x03
+#define ABS_RY 0x04
+#define ABS_RZ 0x05
+#define ABS_THROTTLE 0x06
+#define ABS_RUDDER 0x07
+#define ABS_WHEEL 0x08
+#define ABS_GAS 0x09
+#define ABS_BRAKE 0x0a
+#define ABS_HAT0X 0x10
+#define ABS_HAT0Y 0x11
+#define ABS_HAT1X 0x12
+#define ABS_HAT1Y 0x13
+#define ABS_HAT2X 0x14
+#define ABS_HAT2Y 0x15
+#define ABS_HAT3X 0x16
+#define ABS_HAT3Y 0x17
+#define ABS_PRESSURE 0x18
+#define ABS_DISTANCE 0x19
+#define ABS_TILT_X 0x1a
+#define ABS_TILT_Y 0x1b
+#define ABS_TOOL_WIDTH 0x1c
+
+#define ABS_VOLUME 0x20
+
+#define ABS_MISC 0x28
+
+/*
+ * 0x2e is reserved and should not be used in input drivers.
+ * It was used by HID as ABS_MISC + 6 and userspace needs to detect if
+ * the next ABS_* event is correct or is just ABS_MISC + n.
+ * We define here ABS_RESERVED so userspace can rely on it and detect
+ * the situation described above.
+ */
+#define ABS_RESERVED 0x2e
+
+#define ABS_MT_SLOT 0x2f /* MT slot being modified */
+#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
+#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
+#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
+#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
+#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
+#define ABS_MT_POSITION_X 0x35 /* Center X touch position */
+#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */
+#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
+#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
+#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
+#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
+#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */
+#define ABS_MT_TOOL_X 0x3c /* Center X tool position */
+#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
+
+
+#define ABS_MAX 0x3f
+#define ABS_CNT (ABS_MAX + 1)
+
+/* Switch events */
+#define SW_LID 0x00 /* Set = lid shut */
+#define SW_TABLET_MODE 0x01 /* Set = tablet mode */
+#define SW_HEADPHONE_INSERT 0x02 /* Set = inserted */
+#define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" set = radio enabled */
+#define SW_RADIO SW_RFKILL_ALL /* deprecated */
+#define SW_MICROPHONE_INSERT 0x04 /* Set = inserted */
+#define SW_DOCK 0x05 /* Set = plugged into dock */
+#define SW_LINEOUT_INSERT 0x06 /* Set = inserted */
+#define SW_JACK_PHYSICAL_INSERT 0x07 /* Set = mechanical switch set */
+#define SW_VIDEOOUT_INSERT 0x08 /* Set = inserted */
+#define SW_CAMERA_LENS_COVER 0x09 /* Set = lens covered */
+#define SW_KEYPAD_SLIDE 0x0a /* Set = keypad slide out */
+#define SW_FRONT_PROXIMITY 0x0b /* Set = front proximity sensor active */
+#define SW_ROTATE_LOCK 0x0c /* Set = rotate locked/disabled */
+#define SW_LINEIN_INSERT 0x0d /* Set = inserted */
+#define SW_MUTE_DEVICE 0x0e /* Set = device disabled */
+#define SW_PEN_INSERTED 0x0f /* Set = pen inserted */
+#define SW_MACHINE_COVER 0x10 /* Set = cover closed */
+#define SW_MAX 0x10
+#define SW_CNT (SW_MAX + 1)
+
+/* Misc events */
+#define MSC_SERIAL 0x00
+#define MSC_PULSELED 0x01
+#define MSC_GESTURE 0x02
+#define MSC_RAW 0x03
+#define MSC_SCAN 0x04
+#define MSC_TIMESTAMP 0x05
+#define MSC_MAX 0x07
+#define MSC_CNT (MSC_MAX + 1)
+
+/* LEDs */
+#define LED_NUML 0x00
+#define LED_CAPSL 0x01
+#define LED_SCROLLL 0x02
+#define LED_COMPOSE 0x03
+#define LED_KANA 0x04
+#define LED_SLEEP 0x05
+#define LED_SUSPEND 0x06
+#define LED_MUTE 0x07
+#define LED_MISC 0x08
+#define LED_MAIL 0x09
+#define LED_CHARGING 0x0a
+#define LED_MAX 0x0f
+#define LED_CNT (LED_MAX + 1)
+
+/* Autorepeat values */
+#define REP_DELAY 0x00
+#define REP_PERIOD 0x01
+#define REP_MAX 0x01
+#define REP_CNT (REP_MAX + 1)
+
+/* Sounds */
+#define SND_CLICK 0x00
+#define SND_BELL 0x01
+#define SND_TONE 0x02
+#define SND_MAX 0x07
+#define SND_CNT (SND_MAX + 1)
+
+#endif /* __VIRTIO_INPUT_EVENT_CODES__ */
diff --git a/components/drivers/virtio/virtio_mmio.h b/components/drivers/virtio/virtio_mmio.h
new file mode 100644
index 0000000000..98b92e460c
--- /dev/null
+++ b/components/drivers/virtio/virtio_mmio.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-9-16 GuEe-GUI the first version
+ * 2021-11-11 GuEe-GUI modify to virtio common interface
+ */
+
+#ifndef __VIRTIO_MMIO_H__
+#define __VIRTIO_MMIO_H__
+
+#include <rtdef.h>
+
+struct virtio_mmio_config
+{
+ rt_uint32_t magic; /* [0x00]<RO> Magic value */
+ rt_uint32_t version; /* [0x04]<RO> Device version number */
+ rt_uint32_t device_id; /* [0x08]<RO> Virtio Subsystem Device ID */
+ rt_uint32_t vendor_id; /* [0x0c]<RO> Virtio Subsystem Vendor ID */
+ rt_uint32_t device_features; /* [0x10]<RO> Flags representing features the device supports */
+ rt_uint32_t device_features_sel; /* [0x14]<WO> Device (host) features word selection. */
+ rt_uint32_t res0[2]; /* [0x18] */
+ rt_uint32_t driver_features; /* [0x20]<WO> Device features understood and activated by the driver */
+ rt_uint32_t driver_features_sel; /* [0x24]<WO> Activated (guest) features word selection */
+ rt_uint32_t guest_page_size; /* [0x28]<WO> Guest page size, this value should be a power of 2 */
+ rt_uint32_t res1[1]; /* [0x2c] */
+ rt_uint32_t queue_sel; /* [0x30]<WO> Virtual queue index */
+ rt_uint32_t queue_num_max; /* [0x34]<RO> Maximum virtual queue size */
+ rt_uint32_t queue_num; /* [0x38]<WO> Virtual queue size */
+ rt_uint32_t queue_align; /* [0x3c]<WO> Used Ring alignment in the virtual queue */
+ rt_uint32_t queue_pfn; /* [0x40]<RW> Guest physical page number of the virtual queue */
+ rt_uint32_t queue_ready; /* [0x44]<RW> Virtual queue ready bit */
+ rt_uint32_t res2[2]; /* [0x48] */
+ rt_uint32_t queue_notify; /* [0x50]<WO> Queue notifier */
+ rt_uint32_t res3[3]; /* [0x54] */
+ rt_uint32_t interrupt_status; /* [0x60]<RO> Interrupt status */
+ rt_uint32_t interrupt_ack; /* [0x64]<WO> Interrupt acknowledge */
+ rt_uint32_t res4[2]; /* [0x68] */
+ rt_uint32_t status; /* [0x70]<RW> Device status */
+ rt_uint32_t res5[3]; /* [0x74] */
+ rt_uint32_t queue_desc_low; /* [0x80]<WO> Virtual queue’s Descriptor Area 64 bit long physical address */
+ rt_uint32_t queue_desc_high; /* [0x84]<WO> */
+ rt_uint32_t res6[2]; /* [0x88] */
+ rt_uint32_t queue_driver_low; /* [0x90]<WO> Virtual queue’s Driver Area 64 bit long physical address */
+ rt_uint32_t queue_driver_high; /* [0x94]<WO> */
+ rt_uint32_t res7[2]; /* [0x98] */
+ rt_uint32_t queue_device_low; /* [0xa0]<WO> Virtual queue’s Device Area 64 bit long physical address */
+ rt_uint32_t queue_device_high; /* [0xa4]<WO> */
+ rt_uint32_t res8[21]; /* [0xa8] */
+ rt_uint32_t config_generation; /* [0xfc]<RO> Configuration atomicity value */
+ rt_uint32_t config[]; /* [0x100+]<RO> Configuration space */
+
+/*
+ * According to the compiler's optimization ways, we should force compiler not
+ * to optimization here, but it will cause some compilers generate memory access
+ * instructions fail. So we allow user to choose a toggle of optimize here.
+ */
+#ifdef RT_USING_VIRTIO_MMIO_ALIGN
+} __attribute__((packed));
+#else
+};
+#endif
+
+#endif /* __VIRTIO_MMIO_H__ */
diff --git a/components/drivers/virtio/virtio_net.c b/components/drivers/virtio/virtio_net.c
new file mode 100644
index 0000000000..9b44cf6bb2
--- /dev/null
+++ b/components/drivers/virtio/virtio_net.c
@@ -0,0 +1,314 @@
+/*
+ * 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 <rthw.h>
+#include <rtthread.h>
+#include <cpuport.h>
+
+#ifdef RT_USING_VIRTIO_NET
+
+#include <virtio_net.h>
+
+static rt_err_t virtio_net_tx(rt_device_t dev, struct pbuf *p)
+{
+ rt_uint16_t id;
+ struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)dev;
+ struct virtio_device *virtio_dev = &virtio_net_dev->virtio_dev;
+ struct virtq *queue_tx = &virtio_dev->queues[VIRTIO_NET_QUEUE_TX];
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+
+ id = (queue_tx->avail->idx * 2) % queue_tx->num;
+
+ virtio_net_dev->info[id].hdr.flags = 0;
+ virtio_net_dev->info[id].hdr.gso_type = 0;
+ virtio_net_dev->info[id].hdr.hdr_len = 0;
+ virtio_net_dev->info[id].hdr.gso_size = 0;
+ virtio_net_dev->info[id].hdr.csum_start = 0;
+ virtio_net_dev->info[id].hdr.csum_offset = 0;
+ virtio_net_dev->info[id].hdr.num_buffers = 0;
+
+ pbuf_copy_partial(p, virtio_net_dev->info[id].rx_buffer, p->tot_len, 0);
+
+ virtio_free_desc(virtio_dev, VIRTIO_NET_QUEUE_TX, id);
+ virtio_free_desc(virtio_dev, VIRTIO_NET_QUEUE_TX, id + 1);
+
+ virtio_fill_desc(virtio_dev, VIRTIO_NET_QUEUE_TX, id,
+ VIRTIO_VA2PA(&virtio_net_dev->info[id].hdr), VIRTIO_NET_HDR_SIZE, VIRTQ_DESC_F_NEXT, id + 1);
+
+ virtio_fill_desc(virtio_dev, VIRTIO_NET_QUEUE_TX, id + 1,
+ VIRTIO_VA2PA(virtio_net_dev->info[id].rx_buffer), p->tot_len, 0, 0);
+
+ virtio_submit_chain(virtio_dev, VIRTIO_NET_QUEUE_TX, id);
+
+ virtio_queue_notify(virtio_dev, VIRTIO_NET_QUEUE_TX);
+
+ virtio_alloc_desc(virtio_dev, VIRTIO_NET_QUEUE_TX);
+ virtio_alloc_desc(virtio_dev, VIRTIO_NET_QUEUE_TX);
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+
+ return RT_EOK;
+}
+
+static struct pbuf *virtio_net_rx(rt_device_t dev)
+{
+ rt_uint16_t id;
+ rt_uint32_t len;
+ struct pbuf *p = RT_NULL, *new, *ret = RT_NULL;
+ struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)dev;
+ struct virtio_device *virtio_dev = &virtio_net_dev->virtio_dev;
+ struct virtq *queue_rx = &virtio_dev->queues[VIRTIO_NET_QUEUE_RX];
+
+ while (queue_rx->used_idx != queue_rx->used->idx)
+ {
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+ id = (queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].id + 1) % queue_rx->num;
+ len = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].len - VIRTIO_NET_HDR_SIZE;
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+ if (len > VIRTIO_NET_PAYLOAD_MAX_SIZE)
+ {
+ rt_kprintf("%s: Receive buffer's size = %u is too big!\n", virtio_net_dev->parent.parent.parent.name, len);
+ len = VIRTIO_NET_PAYLOAD_MAX_SIZE;
+ }
+
+ new = pbuf_alloc(PBUF_RAW, len, PBUF_RAM);
+
+ if (p != RT_NULL)
+ {
+ p->next = new;
+ p = p->next;
+ }
+ else
+ {
+ p = new;
+ ret = p;
+ }
+
+ if (p != RT_NULL)
+ {
+#ifdef RT_USING_SMP
+ level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+ rt_memcpy(p->payload, (void *)VIRTIO_PA2VA(queue_rx->desc[id].addr), len);
+
+ queue_rx->used_idx++;
+
+ virtio_submit_chain(virtio_dev, VIRTIO_NET_QUEUE_RX, id - 1);
+
+ virtio_queue_notify(virtio_dev, VIRTIO_NET_QUEUE_RX);
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static rt_err_t virtio_net_init(rt_device_t dev)
+{
+ int i;
+ rt_uint16_t idx[VIRTIO_NET_RTX_QUEUE_SIZE];
+ struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)dev;
+ struct virtio_device *virtio_dev = &virtio_net_dev->virtio_dev;
+ struct virtq *queue_rx, *queue_tx;
+
+ queue_rx = &virtio_dev->queues[VIRTIO_NET_QUEUE_RX];
+ queue_tx = &virtio_dev->queues[VIRTIO_NET_QUEUE_TX];
+
+ virtio_alloc_desc_chain(virtio_dev, VIRTIO_NET_QUEUE_RX, queue_rx->num, idx);
+ virtio_alloc_desc_chain(virtio_dev, VIRTIO_NET_QUEUE_TX, queue_tx->num, idx);
+
+ for (i = 0; i < queue_rx->num; ++i)
+ {
+ rt_uint16_t id = (i * 2) % queue_rx->num;
+ void *addr = virtio_net_dev->info[i].tx_buffer;
+
+ /* Descriptor for net_hdr */
+ virtio_fill_desc(virtio_dev, VIRTIO_NET_QUEUE_RX, id,
+ VIRTIO_VA2PA(addr), VIRTIO_NET_HDR_SIZE, VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE, id + 1);
+
+ /* Descriptor for data */
+ virtio_fill_desc(virtio_dev, VIRTIO_NET_QUEUE_RX, id + 1,
+ VIRTIO_VA2PA(addr) + VIRTIO_NET_HDR_SIZE, VIRTIO_NET_MSS, VIRTQ_DESC_F_WRITE, 0);
+
+ queue_rx->avail->ring[i] = id;
+ }
+ rt_hw_dsb();
+
+ queue_rx->avail->flags = 0;
+ queue_rx->avail->idx = queue_rx->num;
+
+ queue_rx->used_idx = queue_rx->used->idx;
+
+ queue_tx->avail->flags = VIRTQ_AVAIL_F_NO_INTERRUPT;
+ queue_tx->avail->idx = 0;
+
+ virtio_queue_notify(virtio_dev, VIRTIO_NET_QUEUE_RX);
+
+ return eth_device_linkchange(&virtio_net_dev->parent, RT_TRUE);
+}
+
+static rt_err_t virtio_net_control(rt_device_t dev, int cmd, void *args)
+{
+ rt_err_t status = RT_EOK;
+ struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)dev;
+
+ switch (cmd)
+ {
+ case NIOCTL_GADDR:
+ if (args == RT_NULL)
+ {
+ status = -RT_ERROR;
+ break;
+ }
+
+ rt_memcpy(args, virtio_net_dev->config->mac, sizeof(virtio_net_dev->config->mac));
+ break;
+ default:
+ status = -RT_EINVAL;
+ break;
+ }
+
+ return status;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops virtio_net_ops =
+{
+ virtio_net_init,
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ RT_NULL,
+ virtio_net_control
+};
+#endif
+
+static void virtio_net_isr(int irqno, void *param)
+{
+ struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)param;
+ struct virtio_device *virtio_dev = &virtio_net_dev->virtio_dev;
+ struct virtq *queue_rx = &virtio_dev->queues[VIRTIO_NET_QUEUE_RX];
+
+#ifdef RT_USING_SMP
+ rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock);
+#endif
+
+ virtio_interrupt_ack(virtio_dev);
+ rt_hw_dsb();
+
+ if (queue_rx->used_idx != queue_rx->used->idx)
+ {
+ rt_hw_dsb();
+
+ eth_device_ready(&virtio_net_dev->parent);
+ }
+
+#ifdef RT_USING_SMP
+ rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level);
+#endif
+}
+
+rt_err_t rt_virtio_net_init(rt_ubase_t *mmio_base, rt_uint32_t irq)
+{
+ static int dev_no = 0;
+ char dev_name[RT_NAME_MAX];
+ struct virtio_device *virtio_dev;
+ struct virtio_net_device *virtio_net_dev;
+
+ virtio_net_dev = rt_malloc(sizeof(struct virtio_net_device));
+
+ if (virtio_net_dev == RT_NULL)
+ {
+ goto _alloc_fail;
+ }
+
+ virtio_dev = &virtio_net_dev->virtio_dev;
+ virtio_dev->irq = irq;
+ virtio_dev->mmio_base = mmio_base;
+
+ virtio_net_dev->config = (struct virtio_net_config *)virtio_dev->mmio_config->config;
+
+#ifdef RT_USING_SMP
+ rt_spin_lock_init(&virtio_dev->spinlock);
+#endif
+
+ virtio_reset_device(virtio_dev);
+ virtio_status_acknowledge_driver(virtio_dev);
+
+ virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~(
+ (1 << VIRTIO_NET_F_CTRL_VQ) |
+ (1 << VIRTIO_F_RING_EVENT_IDX));
+
+ virtio_status_driver_ok(virtio_dev);
+
+ if (virtio_queues_alloc(virtio_dev, 2) != RT_EOK)
+ {
+ goto _alloc_fail;
+ }
+
+ if (virtio_queue_init(virtio_dev, VIRTIO_NET_QUEUE_RX, VIRTIO_NET_RTX_QUEUE_SIZE) != RT_EOK)
+ {
+ goto _alloc_fail;
+ }
+
+ if (virtio_queue_init(virtio_dev, VIRTIO_NET_QUEUE_TX, VIRTIO_NET_RTX_QUEUE_SIZE) != RT_EOK)
+ {
+ virtio_queue_destroy(virtio_dev, VIRTIO_NET_QUEUE_RX);
+ goto _alloc_fail;
+ }
+
+ virtio_net_dev->parent.parent.type = RT_Device_Class_NetIf;
+#ifdef RT_USING_DEVICE_OPS
+ virtio_net_dev->parent.parent.ops = &virtio_net_ops;
+#else
+ virtio_net_dev->parent.parent.init = virtio_net_init;
+ virtio_net_dev->parent.parent.open = RT_NULL;
+ virtio_net_dev->parent.parent.close = RT_NULL;
+ virtio_net_dev->parent.parent.read = RT_NULL;
+ virtio_net_dev->parent.parent.write = RT_NULL;
+ virtio_net_dev->parent.parent.control = virtio_net_control;
+#endif
+ virtio_net_dev->parent.eth_tx = virtio_net_tx;
+ virtio_net_dev->parent.eth_rx = virtio_net_rx;
+
+ rt_snprintf(dev_name, RT_NAME_MAX, "virtio-net%d", dev_no++);
+
+ rt_hw_interrupt_install(irq, virtio_net_isr, virtio_net_dev, dev_name);
+ rt_hw_interrupt_umask(irq);
+
+ return eth_device_init(&virtio_net_dev->parent, dev_name);
+
+_alloc_fail:
+
+ if (virtio_net_dev != RT_NULL)
+ {
+ virtio_queues_free(virtio_dev);
+ rt_free(virtio_net_dev);
+ }
+ return -RT_ENOMEM;
+}
+#endif /* RT_USING_VIRTIO_NET */
diff --git a/components/drivers/virtio/virtio_net.h b/components/drivers/virtio/virtio_net.h
new file mode 100644
index 0000000000..d00c3edb90
--- /dev/null
+++ b/components/drivers/virtio/virtio_net.h
@@ -0,0 +1,119 @@
+/*
+ * 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 __VIRTIO_NET_H__
+#define __VIRTIO_NET_H__
+
+#ifdef RT_USING_VIRTIO_NET
+
+#include <rtdef.h>
+#include <netif/ethernetif.h>
+
+#include <virtio.h>
+
+#define VIRTIO_NET_QUEUE_RX 0
+#define VIRTIO_NET_QUEUE_TX 1
+#define VIRTIO_NET_RTX_QUEUE_SIZE 16
+#define VIRTIO_NET_RTX_BUF_SIZE 2048
+
+#define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */
+#define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */
+#define VIRTIO_NET_F_CTRL_GUEST_OFFLOADS 2 /* Dynamic offload configuration */
+#define VIRTIO_NET_F_MTU 3 /* Initial MTU advice */
+#define VIRTIO_NET_F_MAC 5 /* Host has given MAC address */
+#define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in */
+#define VIRTIO_NET_F_GUEST_TSO6 8 /* Guest can handle TSOv6 in */
+#define VIRTIO_NET_F_GUEST_ECN 9 /* Guest can handle TSO[6] w/ ECN in */
+#define VIRTIO_NET_F_GUEST_UFO 10 /* Guest can handle UFO in */
+#define VIRTIO_NET_F_HOST_TSO4 11 /* Host can handle TSOv4 in */
+#define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in */
+#define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in */
+#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in */
+#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */
+#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */
+#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */
+#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */
+#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */
+#define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */
+#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Guest can announce device on the network */
+#define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow Steering */
+#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */
+
+#define VIRTIO_NET_F_HASH_REPORT 57 /* Supports hash report */
+#define VIRTIO_NET_F_RSS 60 /* Supports RSS RX steering */
+#define VIRTIO_NET_F_RSC_EXT 61 /* Extended coalescing info */
+#define VIRTIO_NET_F_STANDBY 62 /* Act as standby for another device with the same MAC */
+#define VIRTIO_NET_F_SPEED_DUPLEX 63 /* Device set linkspeed and duplex */
+
+#define VIRTIO_NET_S_LINK_UP (1 << 0)
+#define VIRTIO_NET_S_ANNOUNCE (1 << 1)
+
+#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1
+#define VIRTIO_NET_HDR_F_DATA_VALID 2
+#define VIRTIO_NET_HDR_F_RSC_INFO 4
+
+#define VIRTIO_NET_HDR_GSO_NONE 0
+#define VIRTIO_NET_HDR_GSO_TCPV4 1
+#define VIRTIO_NET_HDR_GSO_UDP 3
+#define VIRTIO_NET_HDR_GSO_TCPV6 4
+#define VIRTIO_NET_HDR_GSO_ECN 0x80
+
+struct virtio_net_hdr
+{
+ rt_uint8_t flags;
+ rt_uint8_t gso_type;
+ rt_uint16_t hdr_len;
+ rt_uint16_t gso_size;
+ rt_uint16_t csum_start;
+ rt_uint16_t csum_offset;
+ rt_uint16_t num_buffers;
+} __attribute__ ((packed));
+
+#define VIRTIO_NET_MSS 1514
+#define VIRTIO_NET_HDR_SIZE (sizeof(struct virtio_net_hdr))
+#define VIRTIO_NET_PAYLOAD_MAX_SIZE (VIRTIO_NET_HDR_SIZE + VIRTIO_NET_MSS)
+
+struct virtio_net_config
+{
+ rt_uint8_t mac[6];
+ rt_uint16_t status;
+ rt_uint16_t max_virtqueue_pairs;
+ rt_uint16_t mtu;
+ rt_uint32_t speed;
+ rt_uint8_t duplex;
+ rt_uint8_t rss_max_key_size;
+ rt_uint16_t rss_max_indirection_table_length;
+ rt_uint32_t supported_hash_types;
+} __attribute__((packed));
+
+struct virtio_net_device
+{
+ struct eth_device parent;
+
+ struct virtio_device virtio_dev;
+
+ struct virtio_net_config *config;
+
+ struct
+ {
+ /* Transmit hdr */
+ struct virtio_net_hdr hdr;
+ /* Transmit buffer */
+ rt_uint8_t tx_buffer[VIRTIO_NET_PAYLOAD_MAX_SIZE];
+ /* Receive buffer */
+ rt_uint8_t rx_buffer[VIRTIO_NET_PAYLOAD_MAX_SIZE];
+ } info[VIRTIO_NET_RTX_QUEUE_SIZE];
+};
+
+rt_err_t rt_virtio_net_init(rt_ubase_t *mmio_base, rt_uint32_t irq);
+
+#endif /* RT_USING_VIRTIO_NET */
+
+#endif /* __VIRTIO_NET_H__ */
diff --git a/components/drivers/virtio/virtio_queue.h b/components/drivers/virtio/virtio_queue.h
new file mode 100644
index 0000000000..1e416af131
--- /dev/null
+++ b/components/drivers/virtio/virtio_queue.h
@@ -0,0 +1,97 @@
+/*
+ * 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 __VIRTIO_QUEUE_H__
+#define __VIRTIO_QUEUE_H__
+
+#include <rtdef.h>
+
+#define VIRTQ_DESC_F_NEXT 1 /* This marks a buffer as continuing via the next field. */
+#define VIRTQ_DESC_F_WRITE 2 /* This marks a buffer as write-only (otherwise read-only). */
+#define VIRTQ_DESC_F_INDIRECT 4 /* This means the buffer contains a list of buffer descriptors. */
+
+/*
+ * The device uses this in used->flags to advise the driver: don't kick me
+ * when you add a buffer. It's unreliable, so it's simply an optimization.
+ */
+#define VIRTQ_USED_F_NO_NOTIFY 1
+
+/*
+ * The driver uses this in avail->flags to advise the device: don't
+ * interrupt me when you consume a buffer. It's unreliable, so it's
+ * simply an optimization.
+ */
+#define VIRTQ_AVAIL_F_NO_INTERRUPT 1
+
+/* Virtqueue descriptors: 16 bytes. These can chain together via "next". */
+struct virtq_desc
+{
+ rt_uint64_t addr; /* Address (guest-physical). */
+ rt_uint32_t len; /* Length. */
+ rt_uint16_t flags; /* The flags as indicated above. */
+ rt_uint16_t next; /* We chain unused descriptors via this, too */
+};
+
+struct virtq_avail
+{
+ rt_uint16_t flags; /* Notifications */
+ rt_uint16_t idx; /* Where the driver would put the next descriptor entry in the ring (modulo the queue size) */
+ rt_uint16_t ring[];
+
+ /*
+ * Only if VIRTIO_F_RING_EVENT_IDX
+ * rt_uint16_t used_event;
+ */
+};
+
+struct virtq_used_elem
+{
+ rt_uint32_t id; /* Index of start of used descriptor chain. */
+ rt_uint32_t len; /* Total length of the descriptor chain which was written to. */
+};
+
+struct virtq_used
+{
+ rt_uint16_t flags;
+ rt_uint16_t idx;
+ struct virtq_used_elem ring[];
+
+ /*
+ * Only if VIRTIO_F_RING_EVENT_IDX
+ * rt_uint16_t avail_event;
+ */
+};
+
+struct virtq
+{
+ rt_uint32_t num;
+
+ struct virtq_desc *desc;
+ struct virtq_avail *avail;
+ struct virtq_used *used;
+
+ /* Helper of driver */
+ rt_uint16_t used_idx;
+ rt_bool_t *free;
+ rt_size_t free_count;
+};
+
+#define VIRTQ_DESC_TOTAL_SIZE(ring_size) (sizeof(struct virtq_desc) * (ring_size))
+/* flags, idx, used_event + ring * ring_size */
+#define VIRTQ_AVAIL_TOTAL_SIZE(ring_size) (sizeof(rt_uint16_t) * 3 + sizeof(rt_uint16_t) * (ring_size))
+/* flags, idx, avail_event + ring * ring_size */
+#define VIRTQ_USED_TOTAL_SIZE(ring_size) (sizeof(rt_uint16_t) * 3 + sizeof(struct virtq_used_elem) * (ring_size))
+
+#define VIRTQ_AVAIL_RES_SIZE (sizeof(rt_uint16_t)) /* used_event */
+#define VIRTQ_USED_RES_SIZE (sizeof(rt_uint16_t)) /* avail_event */
+
+#define VIRTQ_INVALID_DESC_ID RT_UINT16_MAX
+
+#endif /* __VIRTIO_QUEUE_H__ */