Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

linux驱动开发之字符设备--内核和用户空间数据的交换(sysfs)

$
0
0

前言

设备驱动程序中与用户层的接口,除了 read/write/ioctl 方式外,还有sysfs属性。 对于sysfs属性提供的接口,在应用层,可以直接使用 shell 进行交互,而不必进行 编写 C 代码的方式进行对底层的读写。

正文

API接口

struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)

功能:动态的创建 kobject,并注册到 sysfs 文件系统中。
参数:

  • name 在 /sys/下显示的目录
  • parenet :上述目录的父目录
int sysfs_create_group(struct kobject *kobj,const struct attribute_group *grp)

功能: 在kobject 下创建一组目录
参数:

  • kobject :指定的目录
  • grp: 在指定目录下创建group

分析 一下这个 struct attribute_group 数据类型

struct attribute_group {
    const char      *name;
    struct attribute    **attrs;
};

有两个成员,分别是name 和attrs。

struct attribute {
    const char      *name;
    umode_t         mode;
};

以上两个成员,通常先宏下列宏先定义,在取成员 attr 的方式。

#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

看一下 __ATTR 的定义

// (kernel-3.10\include\linux\Device.h)
#define DEVICE_ATTR(_name, _mode, _show, _store) \
    struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

展开后得到

#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name =          \
     .attr = {.name = __stringify(_name), .mode = _mode },  \
    .show   = _show,                    \
    .store  = _store,                   \
}

示例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>

#include <linux/uaccess.h>
//幻数
#define IOCTL_TYPE 'b'

#define COUNT   32

struct  ioctl_arg {
    int val;
    char buf[COUNT];
};
//定义的命令码
#define CMDCTL  _IO(IOCTL_TYPE,0)
#define CMDR    _IOR(IOCTL_TYPE,1,struct ioctl_arg)
#define CMDW    _IOW(IOCTL_TYPE,2,struct ioctl_arg)

#define DEVICE_NAME "cdev_demo"

static struct cdev *pdev = NULL;
static int major = 0;
static int minor = 0;
static int count = 2;


#define BUF_SIZE        (1024)
static char kbuf[BUF_SIZE];
static int  buf_count = 0;

static int cdev_demo_open(struct inode * inode, struct file * file)
{
    printk("%s,%d\n",__func__,__LINE__);
    return 0;
}
static int cdev_demo_release(struct inode *inode, struct file * file)
{
    printk("%s,%d\n",__func__,__LINE__);
    return 0;
}
static ssize_t cdev_demo_read(struct file * file, char __user * buffer, size_t size, loff_t * loff)
{
    printk("%s,%d\n",__func__,__LINE__);

    if(0 == buf_count){
        return -EAGAIN;
    }

    if(buf_count < size){
        size = buf_count;
    }

    if(size == copy_to_user(buffer,kbuf,size)){
        return -EAGAIN;
    }

    buf_count  = 0;

    return size;

}

static ssize_t cdev_demo_write(struct file * file, const char __user * buffer, size_t size, loff_t * loff)
{
    printk("%s,%d\n",__func__,__LINE__);
    printk("buffer=%s   size=%d\n",buffer,size);
    if(size >BUF_SIZE){
        return -ENOMEM;
    }
    if(size == copy_from_user(kbuf,buffer,size)){
        return -EAGAIN;
    }

    buf_count = size;
    return size;
}
//ioctl
static long cdev_demo_ioctl (struct file *filep, unsigned int cmd, unsigned long arg)
{
    static struct ioctl_arg buf;
    printk("%s,%d\n",__func__,__LINE__);
    //分辨不同命令码
    switch(cmd){
        case CMDCTL:
                printk("do CMDCTL\n");
                break;
        case CMDR:
            //使用 _IOC_SIZE()获得命令码中的数据长度
            if(sizeof(buf) != _IOC_SIZE(cmd)){
                return -EINVAL;
            }

            if(sizeof(buf) == copy_to_user((struct ioctl_arg*)arg,&buf,sizeof(struct ioctl_arg))){
                return -EAGAIN;
            }
            printk("do CMDR\n");
            break;
        case CMDW:
            if(sizeof(buf)!= _IOC_SIZE(cmd)){
                 return -EINVAL;
            }

            if(sizeof(buf) == copy_from_user(&buf,(struct ioctl_arg*)arg,sizeof(buf))){
                return -EAGAIN;
            }
            printk("do CMDW\n");
            printk("%d,%s \n",buf.val,buf.buf);
            break;

        default:
            break;

    }

    return 0;
}
static struct file_operations fops ={
    .owner   = THIS_MODULE,
    .open    = cdev_demo_open,
    .release = cdev_demo_release,
    .read    = cdev_demo_read,
    .write   = cdev_demo_write,
    .unlocked_ioctl = cdev_demo_ioctl,
};
//
char cdev_buf[2] = "a";
//定义在 应用层执行 cat 时的操作函数
static ssize_t cdev_demo_show(struct device *dev,struct device_attribute *attr, char *buf)  
{
    char *s = buf;
    s = sprintf(s,"%s",cdev_buf);
    return sizeof(s);
}
//定义在 应用层执行 echo 时的操作函数
static ssize_t cdev_demo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{

    memcpy(cdev_buf,buf,count);
    return count;
}
//利用DEVICE_ATTR定义一个变量
static DEVICE_ATTR(cdev_demo, 0666, cdev_demo_show, cdev_demo_store);

//去除上边定义变量的成员的attr 作为数组成员
static struct attribute  *g[] = {
    &dev_attr_cdev_demo.attr,
    NULL,
};
//定义了attr_group
static struct attribute_group attr_group = {
    .attrs = g,
};
static int __init cdev_demo_init(void)
{
    dev_t dev;
    int ret;
    struct kobject *cdev_obj;
    printk("%s,%d\n",__func__,__LINE__);

    pdev = cdev_alloc();
    if(NULL == pdev){
        printk("cdev_alloc failed.\n");
        return -ENOMEM;
    }

    cdev_init(pdev,&fops);

    ret = alloc_chrdev_region(&dev,minor,count,DEVICE_NAME);
    if(ret){
        printk("alloc_chrdev_region failed.\n");
        goto ERROR_CDEV;
    }
    major = MAJOR(dev);
    ret = cdev_add(pdev, dev,count);
    if(ret) {
        printk("cdev_add failed.\n");
        goto  ERROR_ADD;
    }
    //在 sys下创建  /cdev_demo目录
    cdev_obj = kobject_create_and_add("cdev_demo",NULL);
    if(!cdev_obj){
        return -ENOMEM;
    }
    //在/cdev_demo目录下创建 attr_grop 
    ret = sysfs_create_group(cdev_obj,&attr_group);
    if(ret)
        goto SYSFS_ERR;


    return 0;
SYSFS_ERR:
    kobject_del(cdev_obj);
    kobject_put(cdev_obj);

ERROR_ADD:
    unregister_chrdev_region(dev,count);
ERROR_CDEV:
    cdev_del(pdev);
    return ret;
}
static void __exit cdev_demo_exit(void)
{
    printk("%s,%d\n",__func__,__LINE__);
    unregister_chrdev_region(MKDEV(major,minor),count);

    cdev_del(pdev);
}

module_init(cdev_demo_init);
module_exit(cdev_demo_exit);
MODULE_LICENSE("GPL");

执行 下列命令 查看变化

echo b > /sys/cdev_demo/cdev_demo 
 cat / sys/cdev_demo/cdev_demo 

总结

通过 sys的方式,可以更加方便的查看底层的一些数据。 sys也可以用来进行调试信息。

参考文献

Linux设备模型(2)_Kobject
使用 /sys 文件系统访问 Linux 内核

“`

作者:u013377887 发表于2017/5/25 20:38:52 原文链接
阅读:215 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>