Linux 虚拟文件系统
class_create 和 device_create内核同时提供了class_create()函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create()函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应 device_create()函数,去/sysfs下寻找对应的类从而创建设备节点。此外,利用d
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只需要响应用户空间的操作即可。
configfs
和sysfs
两者可以共存,但不能相互取代。
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
事件,通过两种方式通知到用户层netlink
和uevent_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
目录自动创建文件,我们只需要实现show
和store
函数即可。然后在应用层就能通过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");
更多推荐
所有评论(0)