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()函数,该函数主要有如下几个工作:

  1. 申请一个实时设备,且吧设备假如到实时设备链表中,以便应用层可以根据名字找到对应的驱动。
  2. 调用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

0 0