【Linux驱动编程】Linux字符驱动之sysfs接口实现

1. sysfs

1.1 前言

  在linux系统中,用户空间访问驱动程序一般是以“设备文件”的方式通过“read/write/ioctl”访问,但这种方式有几个明显的缺点。

  • read/write接口功能单一
  • ioctl虽然可以根据cmd参数实现多重功能,但它们都无法直接在shell/mash脚本中被调用,必须通过C语言方式访问
  • ioctl二进制数据接口存在大小端问题,不同平台CPU(32/64)不方便移植

  除了“设备文件”方式,驱动程序还可以实现procfs虚拟文件系统接口,提供给用户访问。procfs访问驱动程序,同样使用的是read/open/ioctl接口,因此也存在“设备文件”方式中的类似问题。

  因此,linux系统从2.6版本内核开始引入一个独立的抽象接口来描述设备和驱动信息,即是sysfs虚拟文件系统。


1.2 什么是sysfs

  sysfs是linux系统下一个基于内存的文件系统,主要功能是将设备(device)和驱动(driver)内容通过文件的方式从内核空间映射到用户空间,方便用户对设备和驱动进行访问和设置。实现了sysfs文件接口,会在指定目录下将驱动读写空间生成一个临时文件,该文件可以直接通过shell命令的“echo”、“cat”访问。例如,在之前“内核态访问EEPROM”文章中,AT24系列的的EEPROM就将存储空间映射为“/sys/busi2c/device/4-0050/eeprom”文件。


1.3 sysfs特性

  sysfs 一般是挂在是“/sys”目录下,sysfs把系统的设备的驱动映射成层次分明的目录结构,方便用户管理设备,顶级目录一般会包括block、bus、drivers、class、power、firmware等子目录。
在这里插入图片描述

图1 Ubuntu16的sysfs

block 系统的块设备符号链接,符号链接指向/sys/devices下的相应目录
bus 系统总线设备,如i2c、spi、platform等,linux系统总线模型由“device”和“driver”组成,因此bus的子目录包括“device”和“driver”目录
class 包含所有注册的到内核的设备类
dev 包含字符设备(char)和块设备(block)的以主次(majior/minor)编码方式描述链接到实际设备(/sys/device)的链接文件
devices 内核对系统中所有设备的分层次表达模型,也是sysfs文件系统管理设备的最重要的目录结构
firmware 系统加载固件机制的对用户空间的接口
fs 用来描述系统中所有的文件系统,包括文件系统本身和按照文件系统分类存放的已挂载点
kernel 存放内核中所有可调整的参数
module 包含当前系统中已加载的模块,包括编译到内核和编译成模块(.ko)的驱动
power 电源管理描述文件和控制接口

  sysfs机制提供了两组接口,一组用于内核将设备文件映射到sysfs文件系统中,另一组用于用户访问设备文件。两组接口显式描述内核对象、对象属性和对象关系与用户空间的关系。

sysfs对内核空间 sysfs对用户空间
内核对象(kobject) 目录
对象属性(attribute) 文件
对象关系(relationship) 链接

2. sysfs驱动接口实现

  以“Linux 字符驱动之platform框架”文章中的字符设备驱动为基础,增加sysfs实现接口。简单回顾下该字符驱动的作用:

  • 实现一个“软驱动”,通过内核一片物理内存交换用户多进程数据;
  • 支持read/write/ioctl函数访问;

  下面实现sysfs接口,可通过脚本命令“echo”、“cat”访问驱动。


2.1 访问接口回调

  该部分主要是是“echo”、“cat”在驱动最终调用的函数。

static ssize_t memory_drv_cat(struct device *dev,struct device_attribute *attr, char *buf)  
{
    char *s = buf;

	if(pmemory_dev->mem_buf == NULL)
	{
		return -1;
	}
    sprintf(s, "%s", pmemory_dev->mem_buf);
    
    return sizeof(s);
}

static ssize_t memory_drv_echo(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	if(pmemory_dev->mem_buf == NULL)
	{
		return -1;
	}
    memcpy(pmemory_dev->mem_buf, buf, count);

    return count;
}

2.2 创建与释放目录(kobject)

2.2.1 使用总线目录

  sysfs默认挂载在“/sys”目录下,对于内核态是“kobject”,对于用户态,一般映射在总线(bus)下对应的设备(device)目录下,如AT24的EEPROM映射在“/sys/busi2c/device/4-0050/”目录下。对于本次的字符驱动,采用的是platform虚拟总线,因此可以映射在“/sys/platform/device/dev-name/”下。

kobject在设备结构体中的描述:
struct platform_device
     —>struct device dev
        —>struct kobject kobj

  设备驱动在创建时,即会创建该目录,因此,不需再手动手动创建,在创建映射文件时直接指定为该目录。

sysfs_create_file(&pdev->dev.kobj, &dev_attr_mem_bin.attr);/* 在总线device下创建文件 */

2.2.2 自定义目录

  如果不使用总线下的目录,可在“/sys”目录下创建自定义目录,创建目录用“kobject_create_and_add”接口,原型位于“kernel/libkobject.c”中。

extern struct kobject * __must_check kobject_create_and_add(const char *name,
						struct kobject *parent);
参数
参数 含义/说明
引用 #include<linux/kobject.h>
name 目录名称
parent 父目录,NULL为默认在/sys目录下
返回 成功返回创建的目录句柄,失败返回NULL
struct kobject *mem_obj;
mem_obj = kobject_create_and_add("mem_sys", NULL);	

2.3 创建与释放文件(attribute)

  内核对象(attribute)映射到用户态就是文件,“struct attribute”结构体描述,其原型如下,常用的元素的文件名称(name)和文件属性(mode)。

struct attribute {
	const char		*name;
	umode_t			mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	bool			ignore_lockdep:1;
	struct lock_class_key	*key;
	struct lock_class_key	skey;
#endif
};

注:
属性mode,即是文件访问权限,可读、可写、可执行,可以用已经定义的宏表示(S_IWUSR | S_IRUGO),也可以用数字表示(如0660)。


宏含义:
S_IRUSR:用户读权限
S_IWUSR:用户写权限
S_IRGRP:用户组读权限
S_IWGRP:用户组写权限
S_IROTH:其他组都权限
S_IWOTH:其他组写权限


  进一步,驱动设备(device)用“struct device_attribute”描述,对应的“show”和“store”则是需驱动工程师实现的函数实体(如2.1节中)。

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);
};

  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,     \
}

  例如,我们用“DEVICE_ATTR”宏定义一个设备描述文件,根据上述宏原型展开后为下面代码。当然直接使用展开后的方式定义,对于刚接触linux编程的人员来说,可读性比较好。

static DEVICE_ATTR(mem_bin, 0660, memory_drv_cat, memory_drv_echo);	

/* 展开后 */
static struct device_attribute dev_attr_mem_bin = {
       .attr = {
.name = "mem_bin",
.mode = 0660
},
.show = memory_drv_cat,
.store = memory_drv_echo,
};

2.3 目录与文件关联

  可以关联到驱动加载时创建的的目录下,可以可以自定义目,后者需先创建目录。“sysfs_create_file”原型位于“kernel/include/linux/sysfs.h”中。

static inline int __must_check sysfs_create_file(struct kobject *kobj,
						 const struct attribute *attr)

  对于本次的例子程序,可以这样实现。

#if 0	/* 自定义目录'/sys/mem_sys' */
	pmemory_dev->mem_obj = kobject_create_and_add("mem_sys", kernel_kobj->parent);	
	if(!pmemory_dev->mem_obj)
	{
		return -ENOMEM;
	}
	sysfs_create_file(pmemory_dev->mem_obj, &dev_attr_mem_bin.attr);
#else	/* 映射到 '/bus/platform/device'目录下 */
	sysfs_create_file(&pdev->dev.kobj, &dev_attr_mem_bin.attr);
#endif

  与之对应的,则是在驱动退出时,释放文件节点。

#if 0
	sysfs_remove_file(pmemory_dev->mem_obj, &dev_attr_mem_bin.attr);
	kobject_del(pmemory_dev->mem_obj);
    kobject_put(pmemory_dev->mem_obj);
#else
	sysfs_remove_file(&pdev->dev.kobj, &dev_attr_mem_bin.attr);
#endif

3. 源码

3.1 测试

   修改Makefile文件,设置编译内核路径为Ubuntu16(64bit),“KERNELDIR = /usr/src/linux-headers-4.15.0-88-generic”,然后编译生成“dev_mem .ko”和“drv_mem.ko”模块,分别insmod到系统中。

  查看映射文件,驱动如正常加载,会在“/sys/bus/platfor/device/dev_mem”目上生成“mem_bin”文件。
在这里插入图片描述

图2 映射文件

  通过“mem_bin”访问驱动。
在这里插入图片描述

图3 通过脚本访问驱动

3.2 源码

【1】https://github.com/Prry/linux-drivers/tree/master/devmem_platform_sysfs


4. 参考

【1】https://blog.csdn.net/yueqian_scut/article/details/47049021?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

【2】https://blog.csdn.net/wade_510/article/details/72084006

【3】http://www.wowotech.net/linux_kenrel/dm_sysfs.html

【4】https://www.xuebuyuan.com/3121849.html

原创文章 128 获赞 147 访问量 36万+

猜你喜欢

转载自blog.csdn.net/qq_20553613/article/details/104556269