1、常用虚拟文件系统

虚拟文件系统用于Linux内核和用户空间的数据交换。

(1)procfs — 提供内核数据结构的接口,挂载在/proc目录。procfs 历史最早,最初就是用来跟内核交互的唯一方式,用来获取处理器-即cpu、内存、设备驱动、进程等各种信息。

(2)sysfs — 导出内核对象,挂载在/sys目录。sysfs 跟 kobject 框架紧密联系, sysfs 是为设备驱动服务的。

mount -t sysfs sys_debug /data/

(3)debugfs — Debugfs exists as a simple way for kernel developers to make information available to user space。debugfs 从名字来看就是为debug而生,所以更加灵活。

mount -t debugfs kernel_debug /data/

(4)configfs —— 最新usb gadget 驱动采用此文件系统。

mount -t configfs config_debug /data/

(5)tracefs —— 用于跟踪Linux 自带log:如 trace_dwc3_gadget_ep_cmd

mount -t tracefs trace_debug /data/

1.1 configfs 和 sysfs

configfs是基于ram的文件系统,与sysfs的功能有所不同。
sysfs是基于文件系统的kernel对象视图,虽然某些属性允许用户读写,但对象是在kernel中创建、注册、销毁,由kernel控制其生命周期。
configfs是一个基于文件系统的内核对象管理器(或称为config_items),config_items在用户空间通过mkdir显式的创建,使用rmdir销毁,在mkdir之后会出现对应的属性,可以在用户空间对这些属性进行读写,与sysfs不同的是,这些对象的生命周期完全由用户空间控制,kernel只需要响应用户空间的操作即可。
configfssysfs两者可以共存,但不能相互取代。

2、sysfs

sysfs 最主要是用来描绘Linux kernel 2.6 中的设备驱动模型。

2.1 创建属性文件 device_create_file

创建属性文件节点的接口有4个:

sysfs_create_file    //通过kobject创建sysfs的节点(后面3个接口最终都会调用此接口,
							参数attr中没有读写操作的接口,所以不使用此接口)。
device_create_file   //为设备创建sys的节点。
bus_create_file     //为总线创建sys的节点。
driver_create_file   //为驱动创建sys的节点。

sysfs_create_group		//创建一个sysfs目录

实例1:

static int device_init(void)
{
	struct device *dbxiong_dev;
	dev_t dbxiong_devt = MKDEV(dbxiong_major,dbxiong_minor);	//创建设备号
	struct class *dbxiong_class;
	//创建设备类别目录:/sys/class/dbxiong_class
	dbxiong_class = class_create(THIS_MODULE,"dbxiong_class");
	
	//创建设备文件:/sys/class/dbxiong_class/dbxiong
	dbxiong_dev = device_create(dbxiong_class,NULL,dbxiong_devt,"dbxiong_drvdata","dbxiong");
	
	//创建属性文件:/sys/class/dbxiong_class/dbxiong/dbxiong_device
	device_create_file(dbxiong_dev,&dev_attr_dbxiong_device);
	return 0;
}

实例2:

//在dev 指向设备所在sys节点目录下创建test 属性文件,
//如/sys/devices/platform/soc/a600000.ssusb/mode(dev_attr_mode)
err = device_create_file(&dev->dev, &dev_attr_test);	
if (err) {
        dev_err(&dev->dev, "sys file creation failed\n");
        return -ENODEV;
}

static ssize_t test_show(struct device *dev,
                   struct device_attribute *attr, char *buf)
{

    int temp[3];
    struct test_data *gpio_data_ptr = dev_get_drvdata(dev);
    temp[0] = gpio_get_value(gpio_data_ptr->gpio0);
    temp[1] = gpio_get_value(gpio_data_ptr->gpio1);
    temp[2] = gpio_get_value(gpio_data_ptr->gpio2);

    return snprintf(buf, 4, "%d%d%d", temp[0], temp[1],
                    temp[2]);
}

static ssize_t test_store(struct device *dev,
           struct device_attribute *attr, const char *buf, size_t size)

{
    struct test *gpio_data_ptr = dev_get_drvdata(dev);
    gpio_set_value(gpio_data_ptr->gpio0,  (buf[0] == '1') ? 1 : 0);
    gpio_set_value(gpio_data_ptr->gpio1,  (buf[1] == '1') ? 1 : 0);
    gpio_set_value(gpio_data_ptr->gpio2,  (buf[2] == '1') ? 1 : 0)
    return size;
}

static DEVICE_ATTR(test, 0664, test_show, test_store);

2.1.1 class_create()

struct class *class_create(struct module *owner, const char *name) 

第一个参数指定类的所有者是哪个模块;
第二个参数指定类名。
如下:

misc_class = class_create(THIS_MODULE, "misc");

将会创建出 /sys/class/misc

2.1.2 device_create()

struct device *device_create(struct class *class, struct device *parent,
                        dev_t devt, const char *fmt, ...)

第一个参数指定所要创建的设备所从属的类;
第二个参数是这个设备的父设备,如果没有就指定为NULL;
第三个参数是设备号;
第四个参数是设备名称;
第五个参数是从设备号。
如下:

misc->this_device = device_create(misc_class, misc->parent, dev, misc, "%s", misc->name);

将会创建出一个文件夹/sys/class/misc/misc->name(如/sys/class/misc/gpio),同时加载模块的时候,用户空间中的udev 会自动响应 device_create()函数,去/sysfs下寻找对应的类从而创建设备节点(一个文件)/dev/misc->name(如/dev/gpio)。

3、udev

udev 就是利用了sysfs 提供的信息来实现所有devfs的功能的,但不同的是udev运行在用户空间中,而devfs却运行在内核空间。

用户态的后台程序(udev)会动态地周期性的扫描/sys目录中的属性项来自动管理设备文件(也称为设备节点),从而在/dev目录会建立或者删除对应的设备文件。

3.1 热拔插事件

udev 利用设备插入或拔出时内核所发出的热拔插事件(Hotplug Event)来工作。在热拔插时,设备的详细信息会由内核通过netlink 套接字发送出来,发出的事情叫做uevent

udev 从内核收到的信息来创建设备文件。

3.2 冷拔插事件

对于冷拔插的设备,Linux 内核提供了sysfs 下面一个uevent 节点,可以往该节点写一个“add”,导致内核重新发送netlink,之后udev 就可以收到冷拔插的netlink 消息了。

3.3 kobject

kobject 可看做是所有总线、设备和驱动的抽象基类,1个kobject 对应sysfs 中的一个目录总线、设备和驱动中的各个attribute 则对应sysfs 中的1个文件attribute 会伴随着show()store() 这两个函数,分别用于读写该attribute 对应的sysfs 文件。

sysfs 中的目录来源于bus_type, device_driver, device, 而目录中的文件则来源于attribute

sysfs中通过uevent机制来实现一个整个热插拔

uevent整个机制相对比较清晰,当有新的device产生时,kobjct产生uevent事件,通过两种方式通知到用户层netlinkuevent_helper,在用户空间种linux提供了两个机制一个是udev(user device)通过nelink接收uevent产生的事件,另外一种方式是通过uevent_helper,该方式主要是通过回调方式通知用户层。

struct device {
	struct kobject kobj;		//每个device都有一个kobj
	struct device		*parent;

	struct device_private	*p;

	const char		*init_name; /* initial name of the device */
	const struct device_type *type;

	struct bus_type	*bus;		/* type of bus device is on */
	struct device_driver *driver;	/* which driver has allocated this
					   device */
	void		*platform_data;	/* Platform specific data, device
					   core doesn't touch it */
	void		*driver_data;	/* Driver data, set and get with
					   dev_set_drvdata/dev_get_drvdata */
}
//调用实例
int device_add(struct device *dev)
{
	struct device *parent;
	struct kobject *kobj;
	
	parent = get_device(dev->parent);			//获取device 的parent
	kobj = get_device_parent(dev, parent);		//获取 parent 的kobj
	if (kobj)
		dev->kobj.parent = kobj;
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);	//add device的kobj到parent 下
	kobject_uevent(&dev->kobj, KOBJ_ADD);	//产生KOBJ_ADD 事件,通知给用户空间
}

4. linux 驱动生成文件节点

linux用户空间和kernel空间是分开,所以上层需要和某个模块驱动交流的时候,就需要驱动来创建一个文件节点。
以下三种方法,第一种和第二种方法使用时一般流程就是open、read/write、ioctl等,第三种方法则可以通过echo/cat命令来读写

4.1 /dev 节点

在这里插入图片描述

4.2 /proc 节点

在这里插入图片描述
在这里插入图片描述
上图中建的节点是/proc/onekey_recovery/last_pressed,如果不用proc_mkdir而只用proc_create的话,则生成的节点是/proc/last_pressed.

4.3 /sys 节点

使用DEVICE_ATTR,可以实现驱动在sys目录自动创建文件,我们只需要实现showstore 函数即可。然后在应用层就能通过cat和echo命令来对sys创建出来的文件进行读写驱动设备,实现交互。

#include <board.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <sound/soc.h>
#include <sound/jack.h>


static  char mybuf[100]="123";
//cat命令时,将会调用该函数
static ssize_t show_my_device(struct device *dev,
         struct device_attribute *attr, char *buf)       
{
    return sprintf(buf, "%s\n", mybuf);
}

//echo命令时,将会调用该函数
static ssize_t set_my_device(struct device *dev,
                 struct device_attribute *attr,
                 const char *buf, size_t len)        
{
    sprintf(mybuf, "%s", buf);
    return len;
}
//定义一个名字为my_device_test的设备属性文件
static DEVICE_ATTR(my_device_test, S_IWUSR|S_IRUSR, show_my_device, set_my_device);
                
struct file_operations mytest_ops={
         .owner  = THIS_MODULE,
};

static int major;
static struct class *cls;
static int mytest_init(void)
{
struct device *mydev;   
major=register_chrdev(0,"mytest", &mytest_ops);
cls=class_create(THIS_MODULE, "mytest_class");
//创建mytest_device设备
mydev = device_create(cls, 0, MKDEV(major,0),NULL,"mytest_device");        
	//在mytest_device设备目录下创建一个my_device_test属性文件
    if(sysfs_create_file(&(mydev->kobj), &dev_attr_my_device_test.attr)){   
            return -1;
}
            
    	return 0;
}

static void mytest_exit(void)
{
         device_destroy(cls, MKDEV(major,0));
         class_destroy(cls);
         unregister_chrdev(major, "mytest");
}

module_init(mytest_init);
module_exit(mytest_exit);
MODULE_LICENSE("GPL");
 
Logo

更多推荐