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

2019-07-12 20:46发布

class="markdown_views prism-atom-one-light">

前言

设备驱动程序中与用户层的接口,除了 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.10includelinuxDevice.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 #include #include #include #include #include #include #include #include //幻数 #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; struct kobject *cdev_obj; static int cdev_demo_open(struct inode * inode, struct file * file) { printk("%s,%d ",__func__,__LINE__); return 0; } static int cdev_demo_release(struct inode *inode, struct file * file) { printk("%s,%d ",__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 ",__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 ",__func__,__LINE__); printk("buffer=%s size=%d ",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 ",__func__,__LINE__); //分辨不同命令码 switch(cmd){ case CMDCTL: printk("do CMDCTL "); 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 "); 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 "); printk("%d,%s ",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; printk("%s,%d ",__func__,__LINE__); pdev = cdev_alloc(); if(NULL == pdev){ printk("cdev_alloc failed. "); return -ENOMEM; } cdev_init(pdev,&fops); ret = alloc_chrdev_region(&dev,minor,count,DEVICE_NAME); if(ret){ printk("alloc_chrdev_region failed. "); goto ERROR_CDEV; } major = MAJOR(dev); ret = cdev_add(pdev, dev,count); if(ret) { printk("cdev_add failed. "); 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 ",__func__,__LINE__); kobject_del(cdev_obj); kobject_put(cdev_obj); 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 内核 “`