目录
简介
用户空间与内核的交互方式,使用copy_from_user(), copy_to_user().
除了这两种交互方式,内核还提供了其他高级的方式,对于写驱动来说很重要。有proc、sysfs、debugfs、netlink、ioctl。
本文学习sysfs
一、sysfs
2.6内核版本有个关键的特性叫 现代设备模型,所有的设备以类似与树形的数据结构呈现。
1、/sys 目录
在ubuntu系统上的/sys目录
root@ubuntu:/sys# ls
block bus class dev devices firmware fs hypervisor kernel module power
sysfs树包括以下内容:
- 每个总线都在系统中体现(也可以是虚拟或伪总线)
- 每个设备在每条总线上体现
- 在总线上,每个设备驱动绑定到一个设备
可以通过它支持的各种总线(PCI、USB、platform、I2C、SPI等)、各种设备、设备本身、块设备视口等来查看系统
2、API
device_create_file()
struct device 存在数据结构 platform_device, pci_device, net_device, usb_device, i2c_client, serial_port 中
有的驱动中没有struct device,则使用platform bus
接着看一下platform设备
platform设备在Soc嵌入式板子中经常用于表示各种各样的设备
# ls /sys/devices/platform/
alarmtimer 'Fixed MDIO bus.0' intel_pmc_core.0 platform-framebuffer.0
reg-dummy
serial8250 eisa.0 i8042 pcspkr power rtc_cmos uevent
3、platform API
platform_device_register_simple()
platform_device_unregister(sysfs_demo_platdev)
4、创建platform总线设备文件
int device_create_file(struct device *dev, const struct device_attribute *attr);
第二个参数
// interface for exporting device attributes
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);
};
导出设备属性的接口,不要再使用copy_[from/to]_user,参数buf是内核空间的buf
show是读回调,store是写回调
初始化device_attribute 结构体,内核提供了几个宏
DEVICE_ATTR(),__ATTR(),DEVICE_ATTR_RW
#define DEVICE_ATTR_RW(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
#define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store)
#define __ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}
通过上述代码,可以创建read-write (RW), read-only (RO), or write-only (WO) sysfs文件
如 static DEVICE_ATTR_RW(sysfs_debug_level); 初始化后的回调函数是 dev_attr_sysfs_debug_level
5、sysfs总结
设置、显示内核特定的值,每个sysfs文件一个值,不太适合debug大量的输出文件。调试输出需要使用debugfs。
二、程序源码
1、创建platform 总线
2、创建两个sysfs文件,sysfs_debug_level可以读写,sysfs_pressure只读
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
// copy_[to|from]_user()
#include <linux/version.h>
#include <linux/uaccess.h>
MODULE_AUTHOR("wy");
MODULE_LICENSE("Dual MIT/GPL");
MODULE_VERSION("0.1");
//创建的sysfs文件夹
#define OURMODNAME "sysfs_simple_intf"
//文件夹下的文件
#define SYSFS_FILE1 sysfs_debug_level
#define SYSFS_FILE3 sysfs_pressure
//互斥体
static DEFINE_MUTEX(mtx);
//全局的静态参数
static int debug_level;
static u32 gpressure;
static struct platform_device *sysfs_demo_platdev;
//回调函数 pressure
static ssize_t sysfs_pressure_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int n;
//互斥体
if (mutex_lock_interruptible(&mtx))
return -ERESTARTSYS;
pr_debug("In the 'show' method: pressure=%u\n", gpressure);
//将gpressure的值存到buf中
n = snprintf(buf, 25, "%u", gpressure);
mutex_unlock(&mtx);
return n;
}
//属性生命sysfs_process
static DEVICE_ATTR_RO(sysfs_pressure);
//debug_level的参数范围
#define DEBUG_LEVEL_MIN 0
#define DEBUG_LEVEL_MAX 2
//读取debug_level
static ssize_t sysfs_debug_level_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int n;
if (mutex_lock_interruptible(&mtx))
return -ERESTARTSYS;
//debug_level值
n = snprintf(buf, 25, "%d\n", debug_level);
mutex_unlock(&mtx);
return n;
}
//写debug_level
static ssize_t sysfs_debug_level_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret = (int)count, prev_dbglevel;
if (mutex_lock_interruptible(&mtx))
return -ERESTARTSYS;
//保存debug_level
prev_dbglevel = debug_level;
if (count == 0 || count > 12) {
ret = -EINVAL;
goto out;
}
//将字符串的值 转化为int类型
ret = kstrtoint(buf, 0, &debug_level);
if (ret)
goto out;
//验证
if (debug_level < DEBUG_LEVEL_MIN || debug_level > DEBUG_LEVEL_MAX) {
debug_level = prev_dbglevel;
ret = -EFAULT;
goto out;
}
ret = count;
out:
mutex_unlock(&mtx);
return ret;
}
static DEVICE_ATTR_RW(sysfs_debug_level);
//初始化
static int __init sysfs_simple_intf_init(void)
{
int stat = 0;
//检查系统有没有配置SYSFS
if (unlikely(!IS_ENABLED(CONFIG_SYSFS))) {
pr_warn("sysfs unsupported! Aborting ...\n");
return -EINVAL;
}
#define PLAT_NAME "sysfs_simple_intf_device"
//创建platform总线下的文件
sysfs_demo_platdev = platform_device_register_simple(PLAT_NAME, -1, NULL, 0);
//错误验证
if (IS_ERR(sysfs_demo_platdev)) {
stat = PTR_ERR(sysfs_demo_platdev);
pr_info("error (%d) registering our platform device, aborting\n", stat);
goto out1;
}
//创建文件debug_level
stat = device_create_file(&sysfs_demo_platdev->dev, &dev_attr_sysfs_debug_level);
if (stat) {
pr_info("device_create_file [1] failed (%d), aborting now\n", stat);
goto out2;
}
//创建文件gpressure
gpressure = 25;
stat = device_create_file(&sysfs_demo_platdev->dev, &dev_attr_sysfs_pressure);
if (stat) {
pr_info("device_create_file [3] failed (%d), aborting now\n", stat);
goto out3;
}
return 0;
out3:
device_remove_file(&sysfs_demo_platdev->dev,&dev_attr_sysfs_pressure);
device_remove_file(&sysfs_demo_platdev->dev, &dev_attr_sysfs_debug_level);
out2:
platform_device_unregister(sysfs_demo_platdev);
out1:
return stat;
}
//删除
static void __exit sysfs_simple_intf_cleanup(void)
{
device_remove_file(&sysfs_demo_platdev->dev, &dev_attr_sysfs_pressure);
device_remove_file(&sysfs_demo_platdev->dev, &dev_attr_sysfs_debug_level);
platform_device_unregister(sysfs_demo_platdev);
}
module_init(sysfs_simple_intf_init);
module_exit(sysfs_simple_intf_cleanup);
程序输出
#:/sys/devices/platform/sysfs_simple_intf_device# cat sysfs_pressure
25#:/sys/devices/platform/sysfs_simple_intf_device# cat sysfs_debug_level
0
#:/sys/devices/platform/sysfs_simple_intf_device# echo 2 > sysfs_debug_level
#:/sys/devices/platform/sysfs_simple_intf_device# cat sysfs_debug_level
2