除了在proc目录里实现驱动的简单接口外,还可以/sys目录下实现驱动接口
//在linux内核里很多结构体都包含有kobject成员. 通常每个kobject对象在/sys/目录下有对应的目录
struct kobject {
const char *name; //显示的目录名
struct list_head entry; //内核里用于管理kobject对象用,通过此成员加入链表
struct kobject *parent; //也就在指定此kobject对象是在什么目录下的
struct kset *kset; //多个同类型(同子系统)的kobject对象的集合
struct kobj_type *ktype; //指定当前kobject对象的类型
struct sysfs_dirent *sd; // 对应的/sys下的目录
struct kref kref; // 引用当前对象的计数
unsigned int state_initialized:1; //是否已初始化
unsigned int state_in_sysfs:1; //是否显示在/sys目录里
unsigned int state_add_uevent_sent:1; //已增加热插拔事件
unsigned int state_remove_uevent_sent:1; // 移除热插拔事件
unsigned int uevent_suppress:1;
};
每个kobject对象表示/sys目录的一个子目录, 此目录里还可以有可读写的文件, 这些文件叫属性(attribute).
每个kobject对象创建时都带有默认的属性, 也可加入自定义的属性.
struct attribute {
const char *name; //文件名
umode_t mode; //权限
...
}
int sysfs_create_file(struct kobject *kobj,const struct attribute *attr); //在kobj目录下, 创建属性文件
void sysfs_remove_file(struct kobject *kobj , const struct attribute *attr); //移除kobj目录下的属性文件
/设备的属性文件/
//在linux内核的驱动模型里的device结构体
struct device {
struct device *parent;
...
struct kobject kobj; //包含有kobject对象, 设备对象在/sys/bus/.../devices下可查看
const char *init_name;
...
}
struct device_attribute {
struct attribute attr; //基于attribute扩展的属性
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); //写属性文件时触发调用的函数
}; //基于attribute扩展而来,以便为设备创建属性文件
int device_create_file(struct device *dev,
const struct device_attribute *attr) //方便为设备增加属性文件的函数
{
int error = 0;
if (dev)
error = sysfs_create_file(&dev->kobj, &attr->attr); //真家伙
return error;
}
void device_remove_file(struct device *dev,
const struct device_attribute *attr) //方便移除设备的属性文件的函数
{
if (dev)
sysfs_remove_file(&dev->kobj, &attr->attr); //真家伙
}
///
如为平台设备增加一属性文件:
test.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sysfs.h>
#include <linux/platform_device.h>
ssize_t myshow(struct device *dev, struct device_attribute *attr,
char *buf)
{
static int n = 0;
sprintf(buf, "n = %d\n", n++);
return strlen(buf);
}
size_t mystore(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
printk("in mystore: %s\n", buf);
return count;
}
struct device_attribute attr = {
.attr = {"hello", 0644},
.show = myshow,
.store = mystore,
};
struct platform_device pdev = {
.name = "haha",
.id = -1,
};
static int __init test_init(void)
{
int ret;
ret = platform_device_register(&pdev);
sysfs_create_file(&pdev.dev.kobj, &attr.attr);
return 0;
}
static void __exit test_exit(void)
{
sysfs_remove_file(&pdev.dev.kobj, &attr.attr);
platform_device_register(&pdev);
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
/
加载模块后, 对”/sys/bus/platform/devices/haha/hello”文件读写即可
/总线的属性文件/
struct bus_type {
const char *name;
...
struct subsys_private *p; // 里面有struct kset类型(kobject对象的集合)的成员: subsys, devices_kset, drivers_kset. 所以在"/sys/bus/xx/"总线目录下有devices目录装载挂载到总线的设备, drivers目录挂载到总线的设备驱动
}
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *bus, char *buf);
ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);
}; //为了方便总线创建属性文件封装的类型
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr) //为总线创建属性文件
{
int error;
if (bus_get(bus)) {
error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr); //真家伙
...
}
EXPORT_SYMBOL_GPL(bus_create_file);
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
{
if (bus_get(bus)) {
sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr); //真家伙
bus_put(bus);
}
}
EXPORT_SYMBOL_GPL(bus_remove_file);
/设备驱动的属性文件/
struct device_driver {
const char *name;
struct bus_type *bus;
...
struct driver_private *p; //里有struct kobject成员kobj, 所以设备驱动注册后也会在总线的drivers目录下有一个名为name的目录.
};
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *driver, char *buf);
ssize_t (*store)(struct device_driver *driver, const char *buf,
size_t count);
}; //为了方便在设备驱动目录创建属性文件封装出来的类型
int driver_create_file(struct device_driver *drv,
const struct driver_attribute *attr)
{
int error;
if (drv)
error = sysfs_create_file(&drv->p->kobj, &attr->attr); //真家伙
...
}
EXPORT_SYMBOL_GPL(driver_create_file);
void driver_remove_file(struct device_driver *drv,
const struct driver_attribute *attr)
{
if (drv)
sysfs_remove_file(&drv->p->kobj, &attr->attr); //真家伙
}
EXPORT_SYMBOL_GPL(driver_remove_file);