xenomai 实时linux驱动编写实例
Posted bigPillow
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了xenomai 实时linux驱动编写实例相关的知识,希望对你有一定的参考价值。
在上个(http://blog.csdn.net/woshidahuaidan2011/article/details/53510510)博文中,介绍了有关xenomai在ubuntu上的安装,接下来根据xenomai代码里面的example修改出一个字符驱动代码及其驱动对应的测试程序。
下面是驱动代码:
#include <linux/module.h>
#include <rtdm/rtdm_driver.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ziv,<woshidahuaidan2011@hotmail.com>");
#define SIZE_MAX 1024
#define DEVICE_NAME "mydriver"
#define SOME_SUB_CLASS 4711
/**
* The context of a device instance
*
* A context is created each time a device is opened and passed to
* other device handlers when they are called.
*
*/
typedef struct buffer_s
int size;
char data[SIZE_MAX];
buffer_t;
/**
* Open the device
*
* This function is called when the device shall be opened.
*
*/
static int simple_rtdm_open_nrt(struct rtdm_dev_context *context,
rtdm_user_info_t * user_info, int oflags)
buffer_t * buffer = (buffer_t *) context->dev_private;
buffer->size = 0; /* clear the buffer */
return 0;
/**
* Close the device
*
* This function is called when the device shall be closed.
*
*/
static int simple_rtdm_close_nrt(struct rtdm_dev_context *context,
rtdm_user_info_t * user_info)
return 0;
/**
* Read from the device
*
* This function is called when the device is read in non-realtime context.
*
*/
static ssize_t simple_rtdm_read_nrt(struct rtdm_dev_context *context,
rtdm_user_info_t * user_info, void *buf,
size_t nbyte)
buffer_t * buffer = (buffer_t *) context->dev_private;
int size = (buffer->size > nbyte) ? nbyte : buffer->size;
buffer->size = 0;
if (rtdm_safe_copy_to_user(user_info, buf, buffer->data, size))
rtdm_printk("ERROR : can't copy data from driver\\n");
return size;
/**
* Write in the device
*
* This function is called when the device is written in non-realtime context.
*
*/
static ssize_t simple_rtdm_write_nrt(struct rtdm_dev_context *context,
rtdm_user_info_t * user_info,
const void *buf, size_t nbyte)
buffer_t * buffer = (buffer_t *) context->dev_private;
buffer->size = (nbyte > SIZE_MAX) ? SIZE_MAX : nbyte;
if (rtdm_safe_copy_from_user(user_info, buffer->data, buf, buffer->size))
rtdm_printk("ERROR : can't copy data to driver\\n");
return nbyte;
int simple_rtdm_ioctl_nrt(struct rtdm_dev_context *context,
rtdm_user_info_t * user_info,
unsigned int request, void *arg)
int *return_size = (int *)arg;
int max_size = SIZE_MAX;
int valid_size = 0;
int err = 0;
buffer_t * buffer = (buffer_t *) context->dev_private;
valid_size = buffer->size;
switch (request)
case 0x01:
err =
rtdm_safe_copy_to_user(user_info, return_size,
&max_size,
sizeof(int));
break;
case 0x02:
err =
rtdm_safe_copy_to_user(user_info, return_size,
&valid_size,
sizeof(int));
break;
default:
err = -ENOTTY;
return err;
/**
* This structure describe the simple RTDM device
*
*/
static struct rtdm_device device =
.struct_version = RTDM_DEVICE_STRUCT_VER,
.device_flags = RTDM_NAMED_DEVICE,
.context_size = sizeof(buffer_t),
.device_name = DEVICE_NAME,
.open_nrt = simple_rtdm_open_nrt,
.ops =
.close_nrt = simple_rtdm_close_nrt,
.read_nrt = simple_rtdm_read_nrt,
.write_nrt = simple_rtdm_write_nrt,
.ioctl_nrt = simple_rtdm_ioctl_nrt,
,
.device_class = RTDM_CLASS_EXPERIMENTAL,
.device_sub_class = SOME_SUB_CLASS,
.profile_version = 1,
.driver_name = "SimpleRTDM",
.driver_version = RTDM_DRIVER_VER(0, 1, 2),
.peripheral_name = "Simple RTDM example",
.provider_name = "trem",
.proc_name = device.device_name,
;
/**
* This function is called when the module is loaded
*
* It simply registers the RTDM device.
*
*/
int __init simple_rtdm_init(void)
return rtdm_dev_register(&device);
/**
* This function is called when the module is unloaded
*
* It unregister the RTDM device, polling at 1000 ms for pending users.
*
*/
void __exit simple_rtdm_exit(void)
rtdm_dev_unregister(&device, 1000);
module_init(simple_rtdm_init);
module_exit(simple_rtdm_exit);
下面对上面的代码做一个说明,驱动加载之后会自己调用
rtdm_dev_register(&device);
暂时先不说rtdm_dev_register函数的作用,先介绍一下其参数device,其对应的结构体如下:
struct rtdm_device
/** Revision number of this structure, see
* @ref drv_versioning "Driver Versioning" defines */
int struct_version;
/** Device flags, see @ref dev_flags "Device Flags" for details */
int device_flags;
/** Size of driver defined appendix to struct rtdm_dev_context */
size_t context_size;
/** Named device identification (orthogonal to Linux device name space) */
char device_name[RTDM_MAX_DEVNAME_LEN + 1];
/** Protocol device identification: protocol family (PF_xxx) */
int protocol_family;
/** Protocol device identification: socket type (SOCK_xxx) */
int socket_type;
/** Named device instance creation for real-time contexts,
* optional (but deprecated) if open_nrt is non-NULL, ignored for
* protocol devices
* @deprecated Only use non-real-time open handler in new drivers. */
rtdm_open_handler_t open_rt;
/** Named device instance creation for non-real-time contexts,
* optional if open_rt is non-NULL, ignored for protocol devices */
rtdm_open_handler_t open_nrt;
/** Protocol socket creation for real-time contexts,
* optional (but deprecated) if socket_nrt is non-NULL, ignored for
* named devices
* @deprecated Only use non-real-time socket creation handler in new
* drivers. */
rtdm_socket_handler_t socket_rt;
/** Protocol socket creation for non-real-time contexts,
* optional if socket_rt is non-NULL, ignored for named devices */
rtdm_socket_handler_t socket_nrt;
/** Default operations on newly opened device instance */
struct rtdm_operations ops;
/** Device class ID, see @ref RTDM_CLASS_xxx */
int device_class;
/** Device sub-class, see RTDM_SUBCLASS_xxx definition in the
* @ref profiles "Device Profiles" */
int device_sub_class;
/** Supported device profile version */
int profile_version;
/** Informational driver name (reported via /proc) */
const char *driver_name;
/** Driver version, see @ref drv_versioning "Driver Versioning" defines */
int driver_version;
/** Informational peripheral name the device is attached to
* (reported via /proc) */
const char *peripheral_name;
/** Informational driver provider name (reported via /proc) */
const char *provider_name;
/** Name of /proc entry for the device, must not be NULL */
const char *proc_name;
#ifdef CONFIG_XENO_OPT_VFILE
/** Set to device's vfile data after registration, do not modify */
struct xnvfile_directory vfroot;
struct xnvfile_regular info_vfile;
#endif
/** Driver definable device ID */
int device_id;
/** Driver definable device data */
void *device_data;
/** Data stored by RTDM inside a registered device (internal use only) */
struct rtdm_dev_reserved reserved;
;
/** @ devregister */
下面对基础几个变量的含义做一下介绍。
首先对于
1、
int device_flags;
这里是表示驱动的类型,其可设置的值有如下三个:
//使用本标记表示仅仅提供应用层一个函数接口
#define RTDM_EXCLUSIVE 0x0001
//设置此标记表示设备通过存文本名字表示
#define RTDM_NAMED_DEVICE 0x0010
//使用此标记,表示设备通过ID表盒及其套接字类型来表示。
#define RTDM_PROTOCOL_DEVICE 0x0020
2、
size_t context_size
表示私有数据的大小,这个大小主要跟如下的结构体有关:
struct rtdm_dev_context
/** Context flags, see @ref ctx_flags "Context Flags" for details */
unsigned long context_flags;
/** Associated file descriptor */
int fd;
/** Lock counter of context, held while structure is referenced by an
* operation handler */
atomic_t close_lock_count;
/** Set of active device operation handlers */
struct rtdm_operations *ops;
/** Reference to owning device */
struct rtdm_device *device;
/** Data stored by RTDM inside a device context (internal use only) */
struct rtdm_devctx_reserved reserved;
/** Begin of driver defined context data structure */
char dev_private[0];
;
可以看到该结构体最后一个数组是0,该内存的分配是是在系统调用rt_dev_open函数的时候,有内核根据context_size的大小来申请内存,具体的实现方式为:
if (nrt_mem)
context = kmalloc(sizeof(struct rtdm_dev_context) +
device->context_size, GFP_KERNEL);
else
context = xnmalloc(sizeof(struct rtdm_dev_context) +
device->context_size);
所以在驱动代码中的write、read、open函数可以直接使用
buffer_t * buffer = (buffer_t *) context->dev_private;
来获取私有内存的地址。
3、
rtdm_open_handler_t open_rt;
rtdm_open_handler_t open_nrt;
open函数打开设备的时候会调用此函数,基本上是做一些初始化的工作。
4、
struct rtdm_operations ops;
文件操作函数,比如write、read、ictcl等等。
具体的函数有如下几个:
/**
* Device operations
*/
struct rtdm_operations
/*! @name Common Operations
* @ */
/** Close handler for real-time contexts (optional, deprecated)
* @deprecated Only use non-real-time close handler in new drivers. */
rtdm_close_handler_t close_rt;
/** Close handler for non-real-time contexts (required) */
rtdm_close_handler_t close_nrt;
/** IOCTL from real-time context (optional) */
rtdm_ioctl_handler_t 8;
/** IOCTL from non-real-time context (optional) */
rtdm_ioctl_handler_t ioctl_nrt;
/** Select binding handler for any context (optional) */
rtdm_select_bind_handler_t select_bind;
/** @ Common Operations */
/*! @name Stream-Oriented Device Operations
* @ */
/** Read handler for real-time context (optional) */
rtdm_read_handler_t read_rt;
/** Read handler for non-real-time context (optional) */
rtdm_read_handler_t read_nrt;
/** Write handler for real-time context (optional) */
rtdm_write_handler_t write_rt;
/** Write handler for non-real-time context (optional) */
rtdm_write_handler_t write_nrt;
/** @ Stream-Oriented Device Operations */
/*! @name Message-Oriented Device Operations
* @ */
/** Receive message handler for real-time context (optional) */
rtdm_recvmsg_handler_t recvmsg_rt;
/** Receive message handler for non-real-time context (optional) */
rtdm_recvmsg_handler_t recvmsg_nrt;
/** Transmit message handler for real-time context (optional) */
rtdm_sendmsg_handler_t sendmsg_rt;
/** Transmit message handler for non-real-time context (optional) */
rtdm_sendmsg_handler_t sendmsg_nrt;
/** @ Message-Oriented Device Operations */
;
6、
int device_class;
说明设备的类型,其主要用如下几个类型:
#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_USB ?
#define RTDM_CLASS_FIREWIRE ?
#define RTDM_CLASS_INTERBUS ?
#define RTDM_CLASS_PROFIBUS ?
#define ...
#define RTDM_CLASS_EXPERIMENTAL 224
#define RTDM_CLASS_MAX 255
在上面的例子中,我们使用的RTDM_CLASS_EXPERIMENTAL,当然也可以自动定义其他的类型、
7、
int device_sub_class;
设备子类,只要跟之前的驱动不重复,可以任意的定义。
这里就简单的介绍这些,其余的根据名字就可以很好地理解其具体含义,
介绍完了struct rtdm_device,接下来简单的介绍下
rtdm_dev_register()函数,该函数主要有如下几个工作:
- 申请一个实时设备,且吧设备假如到实时设备链表中,以便应用层可以根据名字找到对应的驱动。
调用rtdm_proc_register_device函数,在linux 的proc目录项创建相应的驱动文件夹,及其information文件,以便显示驱动信息。
比如加载完本驱动之后,可以使用modinfo命令查看驱动信息的话会得到如下的显示filename: /home/hello/work/ubuntu/xenomai-2.6.4/examples/rtdm/myexample/mydriver/mydriver.ko author: ziv,<woshidahuaidan2011@hotmail.com> license: GPL srcversion: 0D26060235C08390310E6AC depends: vermagic: 3.14.17 SMP mod_unload modversions CORE2
当然也可以直接cat /proc/xenomai/rtdm/mydriver/information得到跟上面一样的信息。
驱动的其他函数比较简单,没什么特殊的,但是那个ioctl函数就是约定0x01获取缓存区最大值,0x02获取缓存区有效值,不过这只是个测试程序,真正的编写驱动代码的是,建议使用_IOXX宏来生成。
接下来就是马克file文件的脚本:
###### CONFIGURATION ######
### List of modules to be build
MODULES = mydriver
###### KERNEL MODULE BUILD (no change required normally) ######
ifneq ($(MODULES),)
### Default to sources of currently running kernel
KSRC ?= /lib/modules/$(shell uname -r)/build
OBJS := $patsubst %, %.o, $(MODULES)
CLEANMOD := $patsubst %, .%*, $(MODULES)
PWD := $(shell if [ "$$PWD" != "" ]; then echo $$PWD; else pwd; fi)
### Kernel 2.6 or 3.0
PATCHLEVEL:=$(shell sed 's/PATCHLEVEL = \\(.*\\)/\\1/;t;d' $(KSRC)/Makefile)
VERSION:=$(shell sed 's/VERSION = \\(.*\\)/\\1/;t;d' $(KSRC)/Makefile)
ifneq ($(VERSION).$(PATCHLEVEL),2.4)
obj-m := $(OBJS)
EXTRA_CFLAGS := -I$(KSRC)/include/xenomai -I$(KSRC)/include/xenomai/posix $(ADD_CFLAGS)
all::
$(MAKE) -C $(KSRC) SUBDIRS=$(PWD) modules
### Kernel 2.4
else
ARCH ?= $(shell uname -i)
INCLUDE := -I$(KSRC)/include/xenomai -I$(KSRC)/include/xenomai/compat -I$(KSRC)/include/xenomai/posix
CFLAGS += $(shell $(MAKE) -s -C $(KSRC) CC=$(CC) ARCH=$(ARCH) SUBDIRS=$(PWD) modules) $(INCLUDE)
all:: $(OBJS)
endif
## Target for capturing 2.4 module CFLAGS
modules:
@echo "$(CFLAGS)"
clean::
$(RM) $(CLEANMOD) *.o *.ko *.mod.c Module*.symvers Module.markers modules.order
$(RM) -R .tmp*
endif
下面看一下应用层,在应用层,比较简单,就是写入读取存放的内容,代码如下:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <rtdm/rtdm.h>
#define DEVICE_NAME "mydriver"
int main(int argc, char *argv)
char buf[1024];
ssize_t size;
int device;
int ret;
/* open the device */
device = rt_dev_open(DEVICE_NAME, 0);
if (device < 0)
printf("ERROR : can't open device %s (%s)\\n",
DEVICE_NAME, strerror(-device));
fflush(stdout);
exit(1);
//得到缓存区最大值
int max_size;
size = rt_dev_ioctl (device, 0x01,&max_size );
printf("get max data size of device %s\\t: %d bytes\\n", DEVICE_NAME, max_size);
int valid_size;
size = rt_dev_ioctl (device, 0x02,&valid_size );
printf("get valid data size of device %s\\t: %d bytes\\n", DEVICE_NAME, valid_size);
/* first write */
sprintf(buf, "HelloWorld!");
size = rt_dev_write (device, (const void *)buf, strlen(buf) + 1);
printf("Write from device %s\\t: %d bytes\\n", DEVICE_NAME, size);
size = rt_dev_ioctl (device, 0x02,&valid_size );
printf("get valid data size of device %s\\t: %d bytes\\n", DEVICE_NAME, valid_size);
/* first read */
size = rt_dev_read (device, (void *)buf, 1024);
printf("Read in device %s\\t: %s\\n", DEVICE_NAME, buf);
/* second read */
size = rt_dev_read (device, (void *)buf, 1024);
printf("Read in device again%s\\t: %d bytes\\n", DEVICE_NAME, size);
/* close the device */
ret = rt_dev_close(device);
if (ret < 0)
printf("ERROR : can't close device %s (%s)\\n",
DEVICE_NAME, strerror(-ret));
fflush(stdout);
exit(1);
return 0;
其makefile文件如下:
###### CONFIGURATION ######
### List of applications to be build
APPLICATIONS = mydrivertest
### Note: to override the search path for the xeno-config script, use "make XENO=..."
###### USER SPACE BUILD (no change required normally) ######
ifneq ($(APPLICATIONS),)
### Default Xenomai installation path
XENO ?= /usr/xenomai
XENOCONFIG=$(shell PATH=$(XENO):$(XENO)/bin:$(PATH) which xeno-config 2>/dev/null)
### Sanity check
ifeq ($(XENOCONFIG),)
all::
@echo ">>> Invoke make like this: \\"make XENO=/path/to/xeno-config\\" <<<"
@echo
endif
CC=$(shell $(XENOCONFIG) --cc)
CFLAGS=$(shell $(XENOCONFIG) --skin=native --cflags) $(MY_CFLAGS)
LDFLAGS=$(MY_LDFLAGS)
LDLIBS=$(shell $(XENOCONFIG) --skin=native --ldflags) \\
$(shell $(XENOCONFIG) --skin=rtdm --ldflags)
# This includes the library path of given Xenomai into the binary to make live
# easier for beginners if Xenomai's libs are not in any default search path.
LDFLAGS+=-Xlinker -rpath -Xlinker $(shell $(XENOCONFIG) --libdir)
all:: $(APPLICATIONS)
clean::
$(RM) $(APPLICATIONS) *.o
endif
对于xenomai就介绍这么多。
至于详细的代码及其注释,请到github下载:
https://github.com/leeshsh/xenomai
https://github.com/leeshsh/xenomai.git
原文地址:
http://blog.csdn.net/woshidahuaidan2011/article/details/53522792
以上是关于xenomai 实时linux驱动编写实例的主要内容,如果未能解决你的问题,请参考以下文章
基于linux+xenomai实时系统Ethercat主站PCIE加速卡