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

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

基于NXP i.MX8 Ethercat实时运动控制器设计 Linux+xenomai+igh+rtnet

xenomai安装

《Linux内核实时性以及网络实时性的研究》

Xenomai PC开发环境