xenomai 实时linux驱动编写实例
来源:互联网 发布:北京鼎泰网络推广公司 编辑:程序博客网 时间:2024/05/20 14:42
在上个(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.koauthor: ziv,<woshidahuaidan2011@hotmail.com>license: GPLsrcversion: 0D26060235C08390310E6ACdepends: 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 buildMODULES = mydriver###### KERNEL MODULE BUILD (no change required normally) ######ifneq ($(MODULES),)### Default to sources of currently running kernelKSRC ?= /lib/modules/$(shell uname -r)/buildOBJS := ${patsubst %, %.o, $(MODULES)}CLEANMOD := ${patsubst %, .%*, $(MODULES)}PWD := $(shell if [ "$$PWD" != "" ]; then echo $$PWD; else pwd; fi)### Kernel 2.6 or 3.0PATCHLEVEL:=$(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.4elseARCH ?= $(shell uname -i)INCLUDE := -I$(KSRC)/include/xenomai -I$(KSRC)/include/xenomai/compat -I$(KSRC)/include/xenomai/posixCFLAGS += $(shell $(MAKE) -s -C $(KSRC) CC=$(CC) ARCH=$(ARCH) SUBDIRS=$(PWD) modules) $(INCLUDE)all:: $(OBJS)endif## Target for capturing 2.4 module CFLAGSmodules: @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 buildAPPLICATIONS = 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 pathXENO ?= /usr/xenomaiXENOCONFIG=$(shell PATH=$(XENO):$(XENO)/bin:$(PATH) which xeno-config 2>/dev/null)### Sanity checkifeq ($(XENOCONFIG),)all:: @echo ">>> Invoke make like this: \"make XENO=/path/to/xeno-config\" <<<" @echoendifCC=$(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) *.oendif
对于xenomai就介绍这么多。
至于详细的代码及其注释,请到github下载:
https://github.com/leeshsh/xenomai
https://github.com/leeshsh/xenomai.git
原文地址:
http://blog.csdn.net/woshidahuaidan2011/article/details/53522792
- xenomai 实时linux驱动编写实例
- 构建Xenomai的Linux实时系统
- 解析Xenomai在实时Linux的应用
- LINUX操作系统嵌入Xenomai实时核
- 构建Xenomai的Linux实时系统
- 基于Xenomai的实时Linux分析与研究
- Linux driver dts使用,实例驱动编写
- Xenomai与Linux设备驱动的中断冲突
- Xenomai 实时线程 select 测试
- Linux I2C设备驱动编写(三)-实例分析AM3359
- Linux I2C设备驱动编写(三)-实例分析AM3359
- Linux I2C设备驱动编写(三)-实例分析AM3359
- Linux I2C设备驱动编写(三)-实例分析AM3359
- Linux I2C设备驱动编写(三)-实例分析AM3359
- Xenomai Linux Exercises
- xenomai 定时器中断测试驱动
- uio驱动编写 实例1
- uio驱动编写 实例1
- oracle学习之:审计
- 搜索命令
- 盒模型、内外边距和边框
- 【NOIP2016提高A组集训第4场11.1】平衡的子集
- 面向对象多方法3
- xenomai 实时linux驱动编写实例
- maven(一)----------创建多模块项目
- 调度器学习笔记三:HSM状态机理解
- 深入类加载器----线程上下文类加载器
- maven(二)----------创建的maven项目没有resource这个目录
- Linux-Tomacat-SSL单双向认证说明文档
- 身所迷茫,心有所虑
- css学习之动画效果
- 请回答1988-最后的德善旁白