diff options
| author | guo <[email protected]> | 2022-12-03 12:07:44 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-12-03 12:07:44 +0800 |
| commit | ecf2d8215961ca7c539132b62a72df6072cc3e14 (patch) | |
| tree | 59005f0f74f2593c0435059264f624c862a89f55 /components/drivers | |
| parent | aaf5462c6d4ae9466a0a8cd2a4bb00cfe1fef2f2 (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')
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(¤t->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__ */ |
