5.1RTDM框架

5.1 RTDM 框架

5.1.1. RTDM的诞生背景与核心目标

自所谓的双内核硬实时 Linux 扩展(如 RTLinux、 RTAI)引入以来,已经开发了大量驱动程序。但是存在接口碎片化,平台移植成本高的问题。尽管许多这些驱动程序针对相似的硬件,但大多数驱动程序定义了私有API,应用层代码与驱动强耦合,更换硬件需重写业务逻辑。由于不存在通用 API,同一硬件(如SJA1000 CAN卡)需为Xenomai、RTAI分别开发驱动,平台移植成本高。

Real-Time Driver Model RTDM 实时驱动模型,旨在统一实时设备驱动程序和使用它们的应用程序的开发接口。

RTDM(Real-Time Driver Model)是Xenomai的一部分,用于支持实时驱动的开放与运行。它的目的就是让开发者在实时内核(Cobalt core)上编写驱动程序时,有一个统一的接口模型,而不是直接依赖Linux的内核驱动框架(例如字符设备、块设备和网络设备)。
这保证了驱动的实时性,避免因为Linux内核的调度或锁机制而引入不可预测的延迟。

独立的驱动模型(separate driver model)

  • RTDM 提供了一套与Linux内核驱动模型分离的API(例如 rtdm_driver, rtdm_device)
  • 驱动通过 RTDM 框架注册到 Xenomai 的实时核心中,而不是注册到 Linux 的 VFS 或设备模型。
  • 应用程序可以通过

独立的注册和调用(separate registration and invocation)

  • 注册(registration)
    驱动作者需要用 RTDM 的 API rtdm_dev_register() 来注册设备
  • 调用 (invocation)
    用户空间程序通过 RTDM 设备文件调用对应的实时驱动。RTDM syscalls (/dev/rtdm/…) 来调用这些驱动,绕过 Linux 的常规路径。

带外感知( Aware )驱动程序
在这里插入图片描述

Xenomai RTDM 驱动的历史和现状问题

  • 很多驱动在 Linux 内核里已经存在 (如 GPIO,CAN, 串口, SPI 等),但这些驱动没有实时保证。
  • Xenomai 社区要么 fork Linux 驱动, 然后改造成 RTDM 驱动,要么单独开放专用的 RTDM 驱动。这些驱动的维护要和 Linux 主线分离, 长期靠 Xenomai 社区或厂商单独维护。

针对设备,RTDM 实现了统一设备抽象层,定义两类标准化设备模型(协议设备/命名设备),覆盖90%实时场景。协议设备,支持面向消息的即通过调用 socket() 进行创建设备。命名设备,支持可用 open() 函数实例化,包括流式设备或控制类设备。为同类硬件(如UART、CAN)定义 Device Profiles 接口规范,实现应用与硬件解耦。

针对设备驱动,RTDM 实现了一个跨平台的 RTOS 服务抽象层,封装了任务调度、同步原语等共性服务,驱动仅依赖 RTDM API。RTOS 服务抽象层允许为任何实现 RTDM 的 Linux 平台开发可移植的设备驱动程序。

RTDM不仅能用于编写设备驱动,还可以用于扩展RTnet协议栈,RTIPC功能等,是对 Cobalt 内核的有效补充,它让 Cobalt 内核更加专注提供核心功能,更加简洁。

5.1.2 RTDM 层次结构

1. RTDM 的角色

应用程序一般总是通过硬件设备驱动程序,来访问硬件设备。RTDM在应用程序和驱动程序之间,充当了一个调解者的角色。

在 RTDM 和应用程序之间,RTDM 提供 high-level 上层API,遵循 POSIX 套接字和 I/O 模型,即用户层API RTDM skin。在 Xenomai3 中,RTDM skinPOSIX skin的别名。通过 xeno-config --skin= 获取编译与链接参数时,选项--skin=rtdm--skin=posix严格等效。

在 RTDM 和驱动程序之间,RTDM 提供 low-level 底层API,旨在提供一个用于构建可移植驱动程序的小型 RTOS 抽象层。

在这里插入图片描述

图中的 Wrapper Library 包装库和 Hardware Abstraction Layer HAL硬件抽象层,并不在 RTDM 的范围内。它们是可以根据需要添加的可选间接层,以实现进一步的抽象。

引入 Wrapper Library 包装库,可以简化相同类别的设备对上层 RTDM API 的使用。

推荐使用 HAL硬件抽象层,可以重用多个驱动程序的公共代码段,例如,附加在不同低级通信适配器驱动程序上的协议堆栈。

Xenomai的双核系统的关键就是保证head(实时域,Out-of-Band)和root(非实时域, In-Band)的连续处理,驱动也是这样。

比如,之前提到的RTDM的锁体系对 Linux-Devetail 硬自旋锁的封装链条
从最底层的硬自旋锁(hard_spinlock_t)-> 管线自旋锁(pipeline_spinlock_t)-> RTDM层的rtdm_lock_t,保证在不同上下文(IRQ/任务/管线)访问共享资源时的原子性和安全性。
驱动的处理方式:

  • 双域驱动涉及
    • RTDM 驱动把核心硬件操作放在 head (OOB)
    • 控制、配置、调度等操作放在 root (非实时域)
  • 连续处理
    • IRQ 先到 head 处理,保证低延迟响应
    • 再同步到 root, 保证 Linux 内核可以看到事件
2. RTDM 堆叠

RTDM 支持并鼓励驱动程序堆叠,如下图所示,其中堆叠了 2 层 RTDM。但驱动程序开发人员仍然可以自由定义适当的其他驱动程序层。

在这里插入图片描述

5.1.3 RTDM 统一设备抽象层

1. 设备模型

以 LInux 的方式看待设备可区分为 3 种基本设备类型:字符模块, 块模块, 或者一个网络模块.

RTDM 支持两种不同类型的设备。它们是根据目前实时 Linux 驱动程序的特性选择的。RTDM 不包含实时块设备模型,甚至不包含文件系统模型。

  • 协议设备 :所有面向消息的设备都属于这一组,类似于 Linux 中的网络模块。

    • 协议设备使用两个标识符进行注册,即协议族和套接字类型。它们根据 POSIX 套接字模型进行寻址,即通过调用 socket()进行创建,通过 close()进行销毁。至少,它们必须提供对发送和接收消息的支持:sendmsg()和 recvmsg(),send()/sendto()和 recv()/recvfrom()在内部映射到这些函数。此外,协议设备的驱动程序可以通过 ioctl()处理发出的请求。
    • ioctl()接口还用于将剩余的套接字调用(如 bind()、connect()、listen()、accept()、shutdown()、getsockopt()、setsockopt()、getsockname()和 getpeername())传递给驱动程序。选择这种映射是为了避免在 RTDM 层中为不常使用的函数创建大量入口点。以 bind() 的代码实现为例,其内部调用了 ioctl()接口。
    COBALT_IMPL(int, bind, (int fd, const struct sockaddr *my_addr, socklen_t addrlen))
    {
      struct _rtdm_setsockaddr_args args = { my_addr, addrlen };
      int ret;
    
      ret = do_ioctl(fd, _RTIOC_BIND, &args);
      if (ret != -EADV && ret != -ENOSYS)
        return set_errno(ret);
    
      return __STD(bind(fd, my_addr, addrlen));
    }
    
  • 命名设备 :这些设备在实时子系统下以唯一的清晰文本名称注册,然后可以通过 open()函数实例化。类似于 Linux 中的字符设备。

    • RTDM 不维护特定的命名层次结构或文件系统。基本上,驱动程序在选择设备名称方面是自由的,但在设备配置文件中为常见类指定了常规的命名方案。
    • 命名设备可以进一步细分为:
      • 流式设备:支持流导向 I/O(read()/write())访问的设备,例如 UART 设备,SPI 设备。
      • 功能设备:仅通过 ioctl()接口提供功能的设备。包括所有不符合消息和流导向模型的设备。
2. 设备规范(Device Profiles)

struct rtdm_devicestruct rtdm_driver 结构体,是对设备和驱动的抽象,统一的结构体,可用简化设备驱动程序的实现并提高其可移植性。

struct rtdm_devicestruct rtdm_driver 结构体成员的定义和取值,与设备自身特点息息相关。针对不同的硬件设备类型,RTDM 为结构体成员变量和成员函数预先定义了取值规范,及衍生出来的必要的数据结构和常量,统称为设备配置规范(Device Profiles)。

  • 设备特性

    • device_flags,设备类型
    /** 如果设置,则应用程序只能请求该设备的一个实例。 */
    #define RTDM_EXCLUSIVE			0x0001
    
    /**
    * 使用在 rtdm_device 描述中提供的固定 minor 进行注册。
    * 如果此标志不存在,则 RTDM 核心会根据注册顺序为由驱动程序管理的设备分配 minor 号。
    */
    #define RTDM_FIXED_MINOR		0x0002
    
    /** 如果设置,则设备通过命名名称进行访问。 */
    #define RTDM_NAMED_DEVICE		0x0010
    
    /** 如果设置,则设备通过协议 ID 和套接字类型组合进行访问。 */
    #define RTDM_PROTOCOL_DEVICE		0x0020
    
    /** 选择设备类型的掩码。 */
    #define RTDM_DEVICE_TYPE_MASK		0x00F0
    
    /** 标志表示 RTDM 的安全变体(尚未支持) */
    #define RTDM_SECURE_DEVICE		0x80000000
    
    • profile_info

      • 宏定义 RTDM_PROFILE_INFO,用于初始化profile_info信息。
      • __name: 类名称,自定义。
      • __id: 类主标识号,用于唯一标识该类。
        #define RTDM_CLASS_PARPORT		1
        #define RTDM_CLASS_SERIAL		2
        #define RTDM_CLASS_CAN			3
        #define RTDM_CLASS_NETWORK		4
        #define RTDM_CLASS_RTMAC		5
        #define RTDM_CLASS_TESTING		6
        #define RTDM_CLASS_RTIPC		7
        #define RTDM_CLASS_COBALT		8
        #define RTDM_CLASS_UDD			9
        #define RTDM_CLASS_MEMORY		10
        #define RTDM_CLASS_GPIO			11
        #define RTDM_CLASS_SPI			12
        #define RTDM_CLASS_PWM			13
        
        #define RTDM_CLASS_MISC			223
        #define RTDM_CLASS_EXPERIMENTAL		224
        #define RTDM_CLASS_MAX			255
        
      • __subid: 类次标识号,用于进一步细分该类,一般由各驱动自行定义。
      • __version: 配置版本号,用于表示该类配置的版本,一般由各驱动自行定义。
    • protocol_family 协议族

      • PF_CAN
      • PF_INET
      • PF_PACKET
      • PF_RTIPC
    • socket_type 套接字类型

      • SOCK_DGRAM
      • SOCK_RAW
      • SOCK_STREAM
  • 支持的操作struct rtdm_fd_ops 中定义了所有可能的操作, 驱动程序按设备需求,按需选择实现哪些操作。由于在实时上下文中与非实时上下文相比,必须使用不同的同步机制和资源分配策略,因此明确区分服务调用上下文至关重要。为了让驱动程序决定如何处理不同的上下文,可以为每个入口类型安装单独的处理程序。如果处理程序是上下文无关的,则也可以为两个入口点注册相同的处理程序。

    • open
    • socket
    • close
    • ioctl_rt
    • ioctl_nrt
    • read_rt
    • read_nrt
    • write_rt
    • write_nrt
    • recvmsg_rt
    • recvmsg_nrt
    • sendmsg_rt
    • sendmsg_nrt
    • select
    • mmap
    • get_unmapped_area
  • 类型和常量 :为描述设备而引入的结构,IOCTL使用的选项或其他数据类型,以及在RTDM上下文中使用的任何常量。

    • 设备必须提供的 IOCTL 选项。
      • 通用的IOCTL选项,定义在 include/rtdm/uapi/rtdm.h 中,包括_RTIOC_GETSOCKOPT,_RTIOC_SETSOCKOPT,_RTIOC_BIND, _RTIOC_CONNECT等。
      • 串口相关的IOCTL选项,定义在 include/rtdm/uapi/serial.h,包括RTSER_RTIOC_GET_CONFIG,RTSER_RTIOC_SET_CONFIG,RTSER_RTIOC_GET_CONTROL等。
      • CAN相关的IOCTL选项,定义在 include/rtdm/uapi/can.h,包括RTCAN_RTIOC_TAKE_TIMESTAMP,RTCAN_RTIOC_RCV_TIMEOUT,RTCAN_RTIOC_SND_TIMEOUT等。
      • GPIO相关的IOCTL选项,定义在 include/rtdm/uapi/gpio.h ,包括GPIO_RTIOC_DIR_OUT,GPIO_RTIOC_DIR_IN,GPIO_RTIOC_IRQEN等。
      • 未其它设备及驱动定义的IOCTL选项,不再一一列举。
    • 其它数据类型和宏定义等,一般定义在 include/rtdm/uapi/rtdm.hinclude/rtdm/uapi/serial.hinclude/rtdm/uapi/can.h等头文件中。
3. 设备注册和调用

通过将 struct rtdm_device *dev 设备描述传递给 rtdm_dev_register()来注册 RTDM 设备。

  1. 注册命名设备
static struct rtdm_driver foo_driver = {
	.profile_info		=	RTDM_PROFILE_INFO(foo,
							  RTDM_CLASS_EXPERIMENTAL,
							  RTDM_SUBCLASS_FOO,
							  42),
	.device_flags		=	RTDM_NAMED_DEVICE|RTDM_EXCLUSIVE,
	.device_count		=	2,
	.context_size		=	sizeof(struct foo_context),
	.ops = {
		.open		=	foo_open,
		.ioctl_rt	=	foo_ioctl_rt,
		.ioctl_nrt	=	foo_ioctl_nrt,
		.close		=	foo_close,
	},
};

static struct rtdm_device foo_devices[2] = {
	[ 0 ... 1 ] = {
    .driver = &foo_driver,
		.label = "foo%d",
	},
};

MODULE_VERSION("1.0.0");
MODULE_DESCRIPTION("Ultra-void IV board driver");
MODULE_AUTHOR'"Whoever");

foo_devices[0].device_data = &some_driver_data0;
ret = rtdm_dev_register(&foo_devices[0]);
...
foo_devices[1].device_data = &some_driver_data1;
ret = rtdm_dev_register(&foo_devices[1]);

这段代码演示了如何基于基于RTDM(Real-Time Device Model)框架,定义和注册一个命名设备的实时设备驱动程序。

首先定义了一个名为 foo_driver 的静态结构体,用于描述驱动程序的基本信息和操作接口。具体来说:

  • profile_info字段包含了驱动程序的配置信息,如驱动名称、类别、子类别和版本号。
  • device_flags字段设置了设备的标志,这里表明设备是命名设备并且是独占的。
  • device_count字段指定了将由该驱动程序控制的设备数量,在这里为2。
  • context_size字段定义了设备上下文的大小,即每个设备的内存需求,这里使用sizeof(struct foo_context)来获取foo_context结构体的大小。
  • ops字段是一个指向结构体的指针,该结构体定义了设备的操作函数,如打开(foo_open)、实时IO控制(foo_ioctl_rt)、非实时IO控制(foo_ioctl_nrt)和关闭(foo_close)。

接下来,定义了一个包含两个设备的静态数组foo_devices,每个设备都关联到上面定义的foo_driver驱动程序,并且其标签被设置为foo%d,这里的%d是一个占位符,实际使用时会被设备的具体编号(0或1)替换。

最后,将两个设备的数据指针分别指向不同的驱动数据结构 some_driver_data0some_driver_data1 ,然后通过 rtdm_dev_register 函数注册这两个设备。

应用程序可以通过以下两种方式打开设备。

推荐的使用方式,Xenomai 应用程序使用实际的设备节点路径来打开RTDM设备。例如:

fd = open("/dev/rtdm/devname", ...);

这种方法不仅符合最新的命名规范,还能避免潜在的兼容性问题和警告信息。

在Xenomai 2.x中, RTDM 设备节点使用的是旧的命名规则,到了 Xenomai 3,RTDM 框架改造后,设备节点的路径和命名方式也有了变化。这意味着,老应用程序如果直接移植到Xenomai 3上,可能会找不到原有的设备节点路径,导致open()失败。

XENO_OPT_RTDM_COMPAT_DEVNODE 是一个布尔类型的配置选项,位于 drivers 菜单下。其默认值为 y,表示启用。该选项允许应用程序在打开RTDM设备时使用旧的命名方案,这样就保证了向后兼容,减少应用移植工作量。

  • fd = open("devname", ...);
  • fd = open("/dev/devname", ...);

当应用程序使用上述旧命名方案打开RTDM设备时,如果内核配置中启用 XENO_OPT_DEBUG_LEGACY,Xenomai 会在内核日志中发出警告信息。

  1. 注册协议设备
static struct rtdm_driver foo_driver = {
	.profile_info		=	RTDM_PROFILE_INFO(foo,
							  RTDM_CLASS_EXPERIMENTAL,
							  RTDM_SUBCLASS_FOO,
							  1),
	.device_flags		=	RTDM_PROTOCOL_DEVICE,
	.device_count		=	1,
	.context_size		=	sizeof(struct foo_context),
	.protocol_family	=	PF_FOO,
	.socket_type		=	SOCK_DGRAM,
	.ops = {
		.socket		=	foo_socket,
		.close		=	foo_close,
		.recvmsg_rt	=	foo_recvmsg,
		.sendmsg_rt	=	foo_sendmsg,
		.ioctl_rt	=	foo_ioctl,
		.ioctl_nrt	=	foo_ioctl,
		.read_rt	=	foo_read,
		.write_rt	=	foo_write,
		.select		=	foo_select,
	},
};

static struct rtdm_device foo_device = {
	.driver = &foo_driver,
	.label = "foo",
	.device_data = &some_driver_data,
};

ret = rtdm_dev_register(&foo_device);
...

MODULE_VERSION("1.0.0");
MODULE_DESCRIPTION("Unexpected protocol driver");
MODULE_AUTHOR'"Whoever");

这段代码演示了如何基于基于RTDM(Real-Time Device Model)框架,定义和注册一个协议设备的实时设备驱动程序。

首先定义了一个名为 foo_driver 的静态结构体,用于描述驱动程序的基本信息和操作接口。具体来说:

  • profile_info: 描述了驱动程序的基本信息,包括驱动名称(foo)、类别(实验性的RTDM_CLASS_EXPERIMENTAL)、子类别(RTDM_SUBCLASS_FOO)和版本号(1)。
  • device_flags: 设备标志,表示设备是一个协议设备(RTDM_PROTOCOL_DEVICE)。
  • device_count: 指定该驱动程序控制的设备数量为1。
  • context_size: 设备上下文的大小,使用sizeof(struct foo_context)来确定。
  • protocol_family: 协议族,这里为PF_FOO,表示使用自定义的协议族。
  • socket_type: 套接字类型,这里是SOCK_DGRAM,表示数据报套接字。
  • ops: 定义了设备的操作函数,包括创建套接字(foo_socket)、关闭设备(foo_close)、实时接收消息(foo_recvmsg)、实时发送消息(foo_sendmsg)、实时IO控制(foo_ioctl_rt)、非实时IO控制(foo_ioctl_nrt)、实时读取(foo_read)、实时写入(foo_write)和选择(foo_select)。

接下来,定义了一个 foo_device 设备,关联到 foo_driver 驱动程序,并且其标签被设置为 foo 。其中,device_data 代表设备数据指针,指向 some_driver_data 结构体。

最后,调用 rtdm_dev_register(&foo_device) 将设备注册到RTDM框架中。

在应用程序中,可直接调用 socket 相关接口使用 RTDM 设备。例如,初始化一个 socket 实例:

txsock = socket(PF_CAN, SOCK_RAW, 0)

具体实例,可以参考 demo/posix/cobalt/can-rtt.c

5.1.4 RTDM RTOS 服务抽象层

为了提高驱动程序的可移植性,RTDM 提供了一个与底层系统无关的通用 API,涵盖基本的 RTOS 服务。该 API 旨在仅提供典型实时驱动程序所需的最小服务集。这有助于保持 RTDM 层的小巧性,并且还提高了其在其他实时 Linux 变体上的可移植性。以下服务组可用:

  • 驱动内联服务 :提供驱动之间可相互调用的接口。
  • 时钟服务 :提供接口用于获取系统时钟或单调时钟,都是以 64 位值表示,单位为纳秒。
  • 任务服务 :此组函数允许驱动程序创建自己的实时任务,挂起用户和驱动程序任务的执行,或操作它们的特性(优先级和周期性)。
  • 定时器服务 :提供定时器服务。
  • 同步服务 :RTDM 提供各种基本同步服务。首先,自旋锁可用于保护小的临界路径,无论它们是否位于中断处理程序中或在非实时上下文中运行。经典的互斥锁和信号量也可用于同步实时任务。同时支持事件机制,可作为信号量的替代方案。
  • 中断管理服务 :对于大多数硬件驱动程序,中断是必不可少的服务。可以使用 RTDM 注册实时中断线的处理程序,并且可以明确启用和禁用这些中断线。
  • 非实时信号服务 :为了将事件从实时域传播到非实时域,可以从 RTDM 层请求特殊的信号服务。从任何上下文触发此类信号都是安全的。一旦没有更多时间关键任务待处理,注册的处理程序将在非实时上下文中执行。
  • 实用工具服务 :此组服务包括实时内存分配、对用户空间内存区域的安全访问、实时安全的内核控制台输出,以及检查当前上下文是否为实时任务等待。

在后续的章节,会详细介绍上述各种服务。

5.1.5 在 Xenomai3 中集成的RTDM驱动

xenomai-v3.2.4/kernel/drivers/ 目录下列出了 Xenomai3 支持的驱动程序,以下是这些子目录及其包含的驱动程序的简要介绍:

  • autotune,包含自动调优驱动程序,用于优化实时系统的性能和配置。
  • serial,包含串行通信驱动程序,支持 UART(通用异步收发传输器)等串行接口设备。
  • can,包含 CAN(Controller Area Network)总线驱动程序,用于支持 CAN 网络接口设备。
  • net,包含网络驱动程序,支持以太网控制器等网络接口设备,用于实时网络通信。
  • analogy,包含模拟输入和输出驱动程序,支持 ADC(模数转换器)和 DAC(数模转换器)等模拟接口设备。
  • ipc,包含进程间通信(IPC)驱动程序,支持实时环境下的消息传递、共享内存等通信机制。
  • udd,包含用户设备驱动(UDD)驱动程序,允许用户空间程序通过 RTDM 接口与设备进行交互。
  • gpio,包含通用输入输出(GPIO)驱动程序,支持硬件上的输入输出引脚。
  • gpiopwm,包含基于 GPIO 的 PWM(脉宽调制)驱动程序,利用 GPIO 引脚实现 PWM 功能。
  • spi,包含 SPI(串行外设接口)总线驱动程序,用于支持 SPI 接口设备。

分别重点介绍串行设备和RTnet,它们分别是命名设备和协议设备的典型代表。

1. 串行设备

通过 read/write 提供对串行设备的访问,是一种命名设备。定义了 IOCTL 以操作输出状态线、获取输入线、等待设备事件,以及配置串行设备的线路特性、超时和事件。

已经支持的串行设备驱动,包括:

  • 16550A UART
  • MPC52xx UART
  • IMX UART

16550A UART驱动为例:

static struct rtdm_driver uart16550A_driver = {
	.profile_info		= RTDM_PROFILE_INFO(uart16550A,
						    RTDM_CLASS_SERIAL,
						    RTDM_SUBCLASS_16550A,
						    RTSER_PROFILE_VER),
	.device_flags		= RTDM_NAMED_DEVICE | RTDM_EXCLUSIVE,
	.device_count		= MAX_DEVICES,
	.context_size		= sizeof(struct rt_16550_context),
	.ops = {
		.open		= rt_16550_open,
		.close		= rt_16550_close,
		.ioctl_rt	= rt_16550_ioctl,
		.ioctl_nrt	= rt_16550_ioctl,
		.read_rt	= rt_16550_read,
		.write_rt	= rt_16550_write,
	},
};

int __init rt_16550_init(void)
{
	struct rtdm_device *dev;

  ...snips...

		dev = kmalloc(sizeof(struct rtdm_device) +
			      RTDM_MAX_DEVNAME_LEN, GFP_KERNEL);
		err = -ENOMEM;
		if (!dev)
			goto cleanup_out;

		dev->driver = &uart16550A_driver;
		dev->label = "rtser%d";

  ...snips...

		err = rtdm_dev_register(dev);

  ...snips...
}

上述代码定义了一个名为 uart16550A_driver 的 RTDM 驱动程序,用于支持 16550A UART 串行通信设备。该驱动程序的配置信息通过 RTDM_PROFILE_INFO 宏初始化,设置了类名称、主标识号、次标识号和版本号。驱动的设备标志包括 RTDM_NAMED_DEVICE 和 RTDM_EXCLUSIVE,表示设备可以通过名称访问且为独占设备。驱动的最大设备数量为 MAX_DEVICES,上下文大小为 sizeof(struct rt_16550_context)。

驱动程序的操作函数包括 open、close、ioctl_rt、ioctl_nrt、read_rt 和 write_rt,分别用于设备的打开、关闭、控制、读取和写入操作。

在 rt_16550_init 函数中,初始化过程中动态分配了一个 rtdm_device 结构体,并将其与 uart16550A_driver 关联。设备的标签设置为 “rtser%d”,然后通过 rtdm_dev_register 函数注册设备。如果设备注册失败,会进行相应的错误处理和清理操作。

2. RTnet 实时网络

RTnet 实时网络分为 2 大部分:实时网络协议堆栈 和 实时网络设备。

实时网络协议堆栈,定义在 kernel/drivers/net/stack/ 目录,由不同的 RTDM 设备组成,及相关的数据结构和IOCTL定义等组成。

当前支持的协议栈包括:

  • 协议设备:实时 UDP
  • 协议设备:实时 TCP
  • 协议设备:实时 Packet

以 UDP 实时协议栈 kernel/drivers/net/stack/ipv4/udp 为例:

static struct rtdm_driver udp_driver = {
    .profile_info =     RTDM_PROFILE_INFO(udp,
                                        RTDM_CLASS_NETWORK,
                                        RTDM_SUBCLASS_RTNET,
                                        RTNET_RTDM_VER),
    .device_flags =     RTDM_PROTOCOL_DEVICE,
    .device_count =	1,
    .context_size =     sizeof(struct rtsocket),

    .protocol_family =  PF_INET,
    .socket_type =      SOCK_DGRAM,

    /* default is UDP */
    .ops = {
        .socket =       rt_inet_socket,
        .close =        rt_udp_close,
        .ioctl_rt =     rt_udp_ioctl,
        .ioctl_nrt =    rt_udp_ioctl,
        .recvmsg_rt =   rt_udp_recvmsg,
        .sendmsg_rt =   rt_udp_sendmsg,
        .select =       rt_socket_select_bind,
    },
};

static struct rtdm_device udp_device = {
	.driver = &udp_driver,
	.label = "udp",
};

static int __init rt_udp_init(void)
{
  ...snips...

	err = rtdm_dev_register(&udp_device);

  ...snips...
}

上述代码首先定义了一个名为 udp_driver 的 RTDM(Real-Time Device Model)驱动程序,用于支持实时环境下的 UDP 网络通信。该驱动程序的配置信息通过 RTDM_PROFILE_INFO 宏初始化,设置了类名称为 udp,主标识号为 RTDM_CLASS_NETWORK,次标识号为 RTDM_SUBCLASS_RTNET,版本号为 RTNET_RTDM_VER

驱动的设备标志为 RTDM_PROTOCOL_DEVICE,表示设备通过协议 ID 和套接字类型进行访问。设备数量为 1,上下文大小为 sizeof(struct rtsocket)。

驱动程序的操作函数包括:

  • socket: 使用 rt_inet_socket 创建套接字。
  • close: 使用 rt_udp_close 关闭套接字。
  • ioctl_rt 和 ioctl_nrt: 使用 rt_udp_ioctl 进行实时和非实时的控制操作。
  • recvmsg_rt: 使用 rt_udp_recvmsg 接收数据报。
  • sendmsg_rt: 使用 rt_udp_sendmsg 发送数据报。
  • select: 使用 rt_socket_select_bind 进行选择操作。

此外,还定义了一个 rtdm_device 结构体 udp_device,并将其与 udp_driver 关联,设备标签设置为 “udp”。

在 rt_udp_init 函数中,初始化过程中注册了 udp_device,使其在实时环境中可用。

实时网络设备,定义在 kernel/drivers/net/drivers/ 目录。用 struct rtnet_device 结构体来定义实时网络设备,由 rt_register_rtnetdev 向实时网络协议堆栈注册网络接口,并调用 RTDM RTOS抽象层 完成相关驱动工作。

当前支持的实时网络设备如下:

  • Intel e1000,e1000e,igb,eepro100等
  • Freescale fec,mpc52xx_fec,mpc8260_fcc_enet,mpc8xx_enet,mpc8xx_fec等
  • RTL r8169,8139too等
  • Cadence MACB/GEM等
  • 其它网口如loopback,tulip,natsemi,via-rhine,at91_ether等等。

5.1.6 RTDM 驱动的设计哲学

5.1.6.1 RTDM 的设计定位:实时优先,而非硬件发现

RTDM 的设计目标是为实时驱动程序提供一个统一的接口模型,而不是一个通用的硬件抽象层(HAL)。RTDM 关注的是驱动程序的实时性和可移植性,而不是硬件发现和管理。

【电力系统】单机无穷大电力系统短路故障暂态稳定Simulink仿真(带说明文档)内容概要:本文档围绕“单机无穷大电力系统短路故障暂态稳定Simulink仿真”展开,提供了完整的仿真模型与说明文档,重点研究电力系统在发生短路故障后的暂态稳定性问题。通过Simulink搭建单机无穷大系统模型,模拟不同类型的短路故障(如三相短路),分析系统在故障期间及切除后的动态响应,包括发电机转子角度、转速、电压和功率等关键参数的变化,进而评估系统的暂态稳定能力。该仿真有助于理解电力系统稳定性机理,掌握暂态过程分析方法。; 适合人群:电气工程及相关专业的本科生、研究生,以及从事电力系统分析、运行与控制工作的科研人员和工程师。; 使用场景及目标:①学习电力系统暂态稳定的基本概念与分析方法;②掌握利用Simulink进行电力系统建模与仿真的技能;③研究短路故障对系统稳定性的影响及提高稳定性的措施(如故障清除时间优化);④辅助课程设计、毕业设计或科研项目中的系统仿真验证。; 阅读建议:建议结合电力系统稳定性理论知识进行学习,先理解仿真模型各模块的功能与参数设置,再运行仿真并仔细分析输出结果,尝试改变故障类型或系统参数以观察其对稳定性的影响,从而深化对暂态稳定问题的理解。
本研究聚焦于运用MATLAB平台,将支持向量机(SVM)应用于数据预测任务,并引入粒子群优化(PSO)算法对模型的关键参数进行自动调优。该研究属于机器学习领域的典型实践,其核心在于利用SVM构建分类模型,同时借助PSO的全局搜索能力,高效确定SVM的最优超参数配置,从而显著增强模型的整体预测效能。 支持向量机作为一种经典的监督学习方法,其基本原理是通过在高维特征空间中构造一个具有最大间隔的决策边界,以实现对样本数据的分类或回归分析。该算法擅长处理小规模样本集、非线性关系以及高维度特征识别问题,其有效性源于通过核函数将原始数据映射至更高维的空间,使得原本复杂的分类问题变得线性可分。 粒子群优化算法是一种模拟鸟群社会行为的群体智能优化技术。在该算法框架下,每个潜在解被视作一个“粒子”,粒子群在解空间中协同搜索,通过不断迭代更新自身速度与位置,并参考个体历史最优解和群体全局最优解的信息,逐步逼近问题的最优解。在本应用中,PSO被专门用于搜寻SVM中影响模型性能的两个关键参数——正则化参数C与核函数参数γ的最优组合。 项目所提供的实现代码涵盖了从数据加载、预处理(如标准化处理)、基础SVM模型构建到PSO优化流程的完整步骤。优化过程会针对不同的核函数(例如线性核、多项式核及径向基函数核等)进行参数寻优,并系统评估优化前后模型性能的差异。性能对比通常基于准确率、精确率、召回率及F1分数等多项分类指标展开,从而定量验证PSO算法在提升SVM模型分类能力方面的实际效果。 本研究通过一个具体的MATLAB实现案例,旨在演示如何将全局优化算法与机器学习模型相结合,以解决模型参数选择这一关键问题。通过此实践,研究者不仅能够深入理解SVM的工作原理,还能掌握利用智能优化技术提升模型泛化性能的有效方法,这对于机器学习在实际问题中的应用具有重要的参考价值。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
【IEEE顶刊复现】水下机器人AUV路径规划和MPC模型预测控制跟踪控制(复现)(Matlab代码实现)内容概要:本文档聚焦于【IEEE顶刊复现】水下机器人AUV路径规划与MPC模型预测控制跟踪控制的研究,提供了完整的Matlab代码实现方案。内容涵盖AUV在复杂海洋环境下的路径规划算法设计与模型预测控制(MPC)的跟踪控制策略,重点复现了高水平期刊中的关键技术细节,包括动力学建模、约束处理、优化求解及控制反馈等环节。文档还附带多个相关科研方向的技术介绍与资源链接,突出其在智能控制与机器人领域的高仿真精度与学术参考价值。; 适合人群:具备一定自动化、控制理论或机器人学背景,熟悉Matlab/Simulink环境,从事科研或工程开发的研究生、高校教师及科研人员;尤其适合致力于路径规划、MPC控制、水下机器人系统开发等相关课题的研究者。; 使用场景及目标:①复现IEEE顶刊中关于AUV路径规划与MPC控制的核心算法;②深入理解MPC在非线性系统中的应用机制与优化求解过程;③为水下机器人、无人系统等方向的科研项目提供可运行的代码基础与技术参考;④辅助论文写作、项目申报与仿真验证。; 阅读建议:建议结合文档中提供的网盘资源(如YALMIP工具包、完整代码等)进行实践操作,重点关注MPC控制器的设计参数设置与路径规划算法的实现逻辑,同时可参考文中列举的其他研究方向拓展思路,提升科研效率与创新能力。
### 如何安装包含 `rtdm_driver.h` 的 RTDM 实时驱动程序 #### 准备工作 为了成功编译和安装 Xenomai 及其 RTDM 驱动,确保操作系统环境已经配置好必要的开发工具链以及内核源码。通常情况下,这涉及到安装一系列依赖包。 对于基于 Debian/Ubuntu 的系统而言,可以通过如下命令来获取所需的基础构建工具: ```bash sudo apt-get update && sudo apt-get install build-essential linux-source libncurses5-dev bison flex libssl-dev ``` #### 获取并编译 Xenomai 源代码 Xenomai 是实现 POSIX 兼容实时扩展层的一个开源项目,而 RTDM (Real-Time Driver Model) 则是 Xenomai 提供的一种用于编写高效能设备驱动框架的一部分[^3]。 下载最新版本的 Xenomai 发行版可以从官方网站或者通过 Git 仓库克隆下来: ```bash git clone https://github.com/xenomai/xenomai.git xenomai-src cd xenomai-src/ ``` 接着按照官方文档说明执行相应的配置选项以适应目标平台需求,并完成编译过程: ```bash ./bootstrap # 初始化自动构建脚本 ./configure --enable-smp --disable-vxworks --prefix=/usr/local/xenomai make -j$(nproc) sudo make install ``` 上述操作会将 Xenomai 库文件放置于 `/usr/local/xenomai/lib` 下面;头文件则位于 `/usr/local/xenomai/include` 中,其中就包含了所需的 `rtdm/driver.h` 文件[^4]。 #### 编写与加载自定义 RTDM 驱动模块 创建一个新的 C 文件作为示例驱动入口点,在此之前先确认已设置正确的 include 路径以便能够访问到刚才提到过的头文件位置。下面给出一段简单的模板代码片段展示如何声明一个基本的任务结构体及其关联函数原型: ```c #include <linux/module.h> #include <rtdm/driver.h> static struct rtdm_task mytask; // Task entry point function prototype... void task_body(void *); int init_module(void){ int ret; printk(KERN_INFO "Loading custom RTDM driver\n"); /* Initialize the real-time task */ ret = rtdm_task_init(&mytask, "MyTask", task_body, NULL, 0, 99, 0); if(ret != 0){ printk(KERN_ERR "Failed to initialize task: %d\n",ret); return ret; } // Start the created task here... return 0; } void cleanup_module(void){ rtdm_task_destroy(&mytask); } ``` 最后一步就是利用标准 Linux 内核模块机制来进行编译链接动作,记得指定 `-I/usr/local/xenomai/include` 参数让 GCC 找到外部库的位置。之后就可以像普通 LKM 一样使用 insmod 命令载入新编写的驱动了。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值