前言
ioctl:注册虚拟的字符设备文件,以这个虚拟设备上的 read/write/ioctl 等接口与用户交互
但这种方式有几个明显的缺点。
read/write接口功能单一,一般只能做一件事情
ioctl虽然可以根据cmd参数实现多重功能,但无法直接在 Shell 脚本中使用,为了使用 ioctl 的功能,还必须编写配套的 C语言的虚拟设备操作程序
ioctl二进制数据接口存在大小端问题 (big endian与little endian),不同平台CPU(32/64)不方便移植
proc:注册 proc 接口,接受用户的 read/write/ioctl 操作;
同样使用的是"read/open/ioctl"接口,因此也存在“设备文件”方式中的类似问题。
而且proc文件系统作为一个调试接口而存在,它提供简单的数据交互,但从内核与用户之间交互的数据量过大(大于Linux页框数据量)就需要特殊处理
sysfs:注册 sysfs 属性
1.什么是sysfs
sysfs是linux系统下一个基于内存的文件系统,主要功能是将设备(device)和驱动(driver)数据内容或属性通过文件的方式从内核空间映射到用户空间,方便用户对设备和驱动进行访问和设置。实现了sysfs文件接口,会在指定目录下将驱动读写空间生成一个临时文件,该文件可以直接通过shell命令的“echo”、“cat”访问。
1.1 sysfs特性
sysfs提供一种机制,使得可以显式描述内核对象、对象属性及对象间关系。sysfs有两组接口,一组针对内核,用于将设备映射到文件系统中,另一组针对用户程序,用于读取或操作这些设备。描述了内核中的sysfs要素及其在用户空间的表现:
sysfs在内核中的组成要素
在用户空间的显示
内核对象(kobject) | 目录 |
对象属性(attribute) | 文件 |
对象关系(relationship) | 链接(Symbolic Link) |
sysfs 一般是挂在是“/sys”目录下,sysfs把系统的设备的驱动映射成层次分明的目录结构,方便用户管理设备,顶级目录一般会包括block、bus、drivers、class、power、firmware等子目录。
devices | 内核对系统中所有设备的分层次表达模型,也是sysfs文件系统管理设备的最重要的目录结构 |
block | 系统的块设备符号链接,符号链接指向/sys/devices下的相应目录 |
bus | 系统总线设备,如i2c、spi、platform等,linux系统总线模型由“device”和“driver”组成,因此bus的子目录包括“device”和“driver”目录 |
class | 包含所有注册的到内核的设备类 |
dev | 包含字符设备(char)和块设备(block)的以主次(majior/minor)编码方式描述链接到实际设备(/sys/device)的链接文件 |
firmware | 系统加载固件机制的对用户空间的接口 |
fs | 用来描述系统中所有的文件系统,包括文件系统本身和按照文件系统分类存放的已挂载点 |
kernel | 存放内核中所有可调整的参数 |
module | 包含当前系统中已加载的模块,包括编译到内核和编译成模块(.ko)的驱动 |
power | 电源管理描述文件和控制接口 |
2.sysfs驱动接口实现
2.1 创建与释放目录(kobject)
2.1.1 使用device目录
sysfs默认挂载在“/sys”目录下,对于内核态是“kobject”,对于用户态,一般映射在总线(bus)下对应的设备(device)目录下,如在“/sys/devices/platform/dev-name/”下。
kobject在设备结构体中的描述:
struct platform_device
—>struct device dev
—>struct kobject kobj
设备驱动在创建时,即会创建该目录,因此,不需再手动手动创建,在创建映射文件时直接指定为该目录。
使用sysfs_create_file在sys下创建设备的属性节点
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr); sysfs_create_file(&pdev->dev.kobj, &mydev_info_attribute);/* 在device下创建属性文件*/
sysfs_create_file(&pdev->dev.kobj, &dev_attr_mydev_info);
2.1.2 自定义目录
如果不使用device下的目录,可在“/sys”目录下创建自定义目录,创建目录用“kobject_create_and_add”接口,原型位于“kernel/libkobject.c”中。
struct kobject_create_and_add(const char *name,struct kobject *parent);
引用 #include<linux/kobject.h>
name 目录名称
parent 父目录,NULL为默认在/sys目录下
返回 成功返回创建的目录句柄,失败返回NULL
struct kobject *soc_kobj;
soc_kobj = kobject_create_and_add("sys_test", NULL);
sysfs_create_file(soc_kobj, &mydev_info_attribute) //在sys/sys_test/创建
//属性文件mydev_info
注意:使用自定义目录作为kobject时,由于该创建的kobject目录没有对应的父设备,不可使用device_attribute
2.2 初始化attribute相关结构体
2.2.1 attribute结构体描述
内核对象(attribute)映射到用户态就是文件,一个简单的属性结构定义“struct attribute”,其原型如下,常用的元素的文件名称(name)和文件属性(mode)。
struct attribute {
const char *name; // 属性文件的名字
struct module *owner; // 属性文件的所有者
mode_t mode;
};
2.2.2 kobj_attribute
一个单独的属性结构并不包含读写其属性值的方法,对于kobject对象类型的属性
struct kobj_attribute {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
char *buf);
ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count);
};static struct kobj_attribute mydev_info_attribute= __ATTR(mydev_info,
0666, demo_info_show, demo_info_store);
2.2.3 device_attribute
1. 驱动设备(device)用“struct device_attribute”描述,对应的“show”和“store”则是需驱动工程师实现的函数实体
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
2. linux内核定义了“DEVICE_ATTR”辅助宏,方便定义device_attribute描述变量
kernel/include/linux/device.h
/* DEVICE_ATTR */
#define DEVICE_ATTR(_name, _mode, _show, store)
struct device_attribute dev_attr##_name = __ATTR(_name, _mode, _show, _store)
/* __ATTR */
#define __ATTR(_name,_mode,_show,_store) {
.attr = {.name = __stringify(_name), .mode = _mode },
.show = _show,
.store = _store,
}
3. 用“DEVICE_ATTR”宏定义一个设备描述文件
static DEVICE_ATTR(mydev_info, 0660, demo_info_show, demo_info_store);
/* 展开后 */
static struct device_attribute dev_attr_mydev_info = {
.attr = {
.name = "mydev_info",
.mode = 0660
},
.show = demo_info_show,
.store = demo_info_store,
};
2.3 访问接口函数
该部分主要是是“echo”、“cat”在驱动最终调用的函数。
2.3.1 kobj_attribute 回调函数
对象是kobject 和kobj_attribute
static ssize_t demo_info_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "demo_info_show test %d\n", demo_info);
}
static ssize_t demo_info_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t count)
{
printk("\n from user,length=0x%X,content=%s\n",count,buf);
return count;
}
2.3.1 device_attribute 回调函数
对象是device和device_attribute
static ssize_t demo_info_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, " demo_info_show test %d\n", demo_info);
}
static ssize_t demo_info_store(struct device *dev,
struct device_attribute *attr, char *buf)
{
printk("\n from user,length=0x%X,content=%s\n",count,buf);
return count;
}
2.4 创建属性组
2.4.1 sysfs_create_group
int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);
void sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp);
sysfs_create_group(&pdev->dev.kobj, &touch_attr_group);/* 在device下创建属性文件组 */
2.4.2 定义对应的attribute结构体
static DEVICE_ATTR(device_file, 0666, tp_info_read, tp_info_write);
static DEVICE_ATTR(device_file0, 0444, tp_read, NULL);
static struct attribute *tp_attrs[] = {
&dev_attr_device_file.attr,
&dev_attr_device_file0.attr,
&dev_attr_device_file1.attr,
&dev_attr_device_file2.attr,
NULL属性结构体数组最后一项必须以NULL结尾。
};
static const struct attribute_group touch_attr_group= {
.attrs = tp_attrs,
};
3.其他创建文件的方式
3.1 device_create_file创建属性
devices (include/linux/device.h)
int device_create_file(struct device *dev, const struct device_attribute *attr)
void device_remove_file(struct device *, struct device_attribute *);
device_create_file(&pdev->dev, &dev_attr_demo); DEVICE_ATTR(_name, _mode, _show, _store);
3.2 driver_create_file
int driver_create_file(struct device_driver *, struct driver_attribute *);
void driver_remove_file(struct device_driver *, struct driver_attribute *);
DRIVER_ATTR(_name, _mode, _show, _store)struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *, char * buf);
ssize_t (*store)(struct device_driver *, const char * buf,
size_t count);
};
3.3 bus_create_file
int bus_create_file(struct bus_type *, struct bus_attribute *);
void bus_remove_file(struct bus_type *, struct bus_attribute *);
BUS_ATTR(_name, _mode, _show, _store)
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *, char * buf);
ssize_t (*store)(struct bus_type *, const char * buf);
};