Overview
Linux 的 sysfs 檔案系統一般 mount 在 /sys 目錄。本文主要介紹 sysfs 檔案系統中裝置驅動模型的建立過程,核心版本 2.6.29 。
裝置驅動資訊主要用來表示裝置以及驅動的層次關系,以及處理熱插拔等。 /sys 中與之相關的資料有:
class 代表一類裝置,比如 mtd 、 net 、 tty 等等
bus 總線,比如 PCI 、 USB 、 I2C 等
device 代表一個裝置
driver 代表一個驅動
以下是一些 sysfs 中的全局變量:
// /sys/class
struct kset * class_kset = kset_create_and_add("class", NULL, NULL);
// /sys/bus
struct kset * bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
// /sys/devices
struct kset * devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
1. Class
1.1 class 的基本結構
struct class {
const char *name;
struct module *owner;
struct class_attribute *class_attrs;
struct device_attribute *dev_attrs;
struct kobject *dev_kobj;
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
struct pm_ops *pm;
struct class_private *p;
};
struct class_private {
struct kset class_subsys;
struct list_head class_devices;
struct list_head class_interfaces;
struct kset class_dirs;
struct mutex class_mutex;
struct class *class;
};
class 在 sysfs 中的層次由 struct class_private 決定。 struct class 隻是 class_private 的封裝。
struct class_private::class_subsys.kobj.kset = class_kset; // 父目錄為 /sys/class
struct class_private::class_subsys.kobj->name 代表這個 class 在 /sys/class 中顯示的名字
struct class::dev_attrs 為裝置屬性,往 class 中添加裝置的時候,這些屬性會自動添加到裝置目錄下。
1.2 建立 class
建立 class 有兩種方法:靜态建立和動态建立。
l 靜态建立
static struct class i2c_adapter_class = {
.owner = THIS_MODULE,
.name = "i2c-adapter",
.dev_attrs = i2c_adapter_attrs,
.class_attrs = ...
};
int retval = class_register(&i2c_adapter_class) ;
l 動态建立
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
class_create 配置設定申請一塊空間給 class , 然後對 name 、 owner 和 class_release 函數指派 , 并最終調用 class_register 。
class_register
根據 struct class 的值,設定 struct class_private
調用 add_class_attrs 在 class 中添加屬性。
l class attrs
class 的屬性最終是在 /sys/class/<new class>/ 目錄下以檔案的形式存在。使用者程式可以直接對這些屬性進行讀寫。如果要靜态建立屬性,可以在定義 class 時對 .class_attrs 域指派 , 使其指向要添加的 attr 數組。如果要動态建立。可以通過函數 class_create_file 添加。
int class_create_file(struct class *cls, const struct class_attribute *attr);
如果是動态建立的屬性,需要在子產品解除安裝時調用 class_remove_file 釋放。
如果是靜态建立的屬性,在調用 class_unregister 時會自動釋放。
2. Bus
2.1 bus 的基本結構
struct bus_type {
const char *name;
struct bus_attribute *bus_attrs;
struct device_attribute *dev_attrs;
struct driver_attribute *drv_attrs;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*suspend_late)(struct device *dev, pm_message_t state);
int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev);
struct pm_ext_ops *pm;
struct bus_type_private *p;
};
struct bus_type_private {
struct kset subsys;
struct kset *drivers_kset;
struct kset *devices_kset;
struct klist klist_devices;
struct klist klist_drivers;
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
};
與 class 類似, bus 在 sysfs 中的顯示由 struct bus_type_private 決定, struct bus_type 隻是一個封裝。
struct bus_type_private::subsys.kobj 代表 /sys/bus/<bus> 目錄。
struct bus_type_private::subsys.kobj.kset = bus_kset; // 預設父目錄為 /sys/bus/
struct bus_type_private::subsys.kobj.ktype = &bus_ktype; // bus 的屬性操作
struct bus_type_private::subsys.kobj.name = <bus 在 /sys/bus/ 目錄下顯示的名字 >;
在 /sys/bus/ 目錄,每建立成功一個 <bus> ,都會自動建立兩個子目錄 drivers 和 devices ,分别代表連到此 <bus> 的裝置和驅動。在 drivers 和 devices 子目錄下,每建立一個 driver ,會把 struct bus_type 中的 drv_attrs 屬性賦給那個 driver ;每建立一個 device ,會把 struct bus_type 中 dev_attrs 賦給那個 device 。
2.2 建立 bus
struct bus_type i2c_bus_type = {
.name = "i2c",
.dev_attrs = i2c_dev_attrs,
.match = i2c_device_match,
.uevent = i2c_device_uevent,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.suspend = i2c_device_suspend,
.resume = i2c_device_resume,
.bus_attr = …
};
int ret = bus_register(&i2c_bus_type);
int bus_register(struct bus_type *bus);
配置設定記憶體給 struct bus_type_private;
根據 struct bus_type 的域設定 bus_type_private;
根據 .bus_attr 設定 bus 的屬性,這些屬性在 bus_unregister 時會被自動釋放。( bus 屬性也可通過 bus_create_file 動态添加,但所有動态添加的屬性都要在解除安裝時通過 bus_remove_file 釋放。)
3 . Device
3.1 Device 的基本結構
struct device {
struct klist klist_children;
struct klist_node knode_parent;
struct klist_node knode_driver;
struct klist_node knode_bus;
struct device *parent;
struct kobject kobj;
char bus_id[BUS_ID_SIZE];
const char *init_name;
struct device_type *type;
unsigned uevent_suppress:1;
struct semaphore sem;
struct bus_type *bus;
struct device_driver *driver;
void *driver_data;
void *platform_data;
struct dev_pm_info power;
u64 *dma_mask;
u64 coherent_dma_mask;
struct device_dma_parameters *dma_parms;
struct list_head dma_pools;
struct dma_coherent_mem *dma_mem;
struct dev_archdata archdata;
spinlock_t devres_lock;
struct list_head devres_head;
struct list_head node;
struct class *class;
dev_t devt;
struct attribute_group **groups;
void (*release)(struct device *dev);
};
3.2 device_register
int device_register(struct device *dev) ;
此函數将 device 登記到 sysfs 中。在調用之前,需對 struct device 進行初始化。
struct device dev ;
dev. parent = <parent dev> ; // 父裝置
dev.release = <func release> ; // 登記釋放 dev 時調用的回調函數
dev.class = <class> ; // struct class
dev.bus = <bus> ; // 所屬總線
然後調用 device_register(&dev) ;
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
device_initialize
做一些初始化工作, dev->kobj.kset = devices_kset ; // 代表 /sys/device 目錄
device_add
// 設定 dev->kobj.parent ,即确定這個 dev 的父目錄,詳情見下節
setup_parent(dev, dev->parent);
// 将 dev 挂到 dev->kobj.parent 代表的目錄 , 如果沒有 parent , 父目錄預設被設定成 dev->kobj.kset 代表的目錄
kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);
device_create_file(dev, &uevent_attr) ; // 給裝置添加 uevent 屬性
if (MAJOR(dev->devt))
device_create_file(dev, &devt_attr); // 給裝置添加 dev 屬性(列印主從裝置号)
device_create_sys_dev_entry(dev); // 在裝置的 class 下建立裝置連結,比如 /sys/char 和 /sys/block ,連結名字為 major:minor ;如果裝置沒有 class ,預設為 /sys/char 目錄
device_add_class_symlinks(dev);
// 在裝置目錄下建立 subsystem 連結,指向其所屬的 class
sysfs_create_link(&dev->kobj, &dev->class->p->class_subsys.kobj, "subsystem");
// 在裝置所屬 class 目錄下建立指向裝置的連結,以裝置名命名
sysfs_create_link(&dev->class->p->class_subsys.kobj, &dev->kobj, dev->bus_id);
// 如果父裝置存在,在裝置目錄下建立指向父裝置的連結,以 device 命名
sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
device_add_attrs(dev);
// 如果有 class ,把 class 中 dev_attrs 屬性都加上
device_add_attributes(dev, class->dev_attrs);
// 如果有 type ,把 type 中 dev_attrs 屬性都加上
device_add_groups(dev, type->groups);
// 把 device 中 groups 指向的屬性都加上
device_add_groups(dev, dev->groups);
bus_add_device(dev); // 在裝置有 bus 時有效
// 如果有 bus ,将 bus 中 dev_attrs 都加上
device_add_attrs(bus_get(dev->bus), dev);
// 在 bus 中建立指向裝置的連結,以裝置名命名
sysfs_create_link(&bus->p->devices_kset->kobj, &dev->kobj, dev->bus_id);
// 在裝置中建立指向總線的連結,以 ”subsystem” 命名
sysfs_create_link(&dev->kobj, &dev->bus->p->subsys.kobj, "subsystem");
dpm_sysfs_add(dev); // 建立 power 屬性
device_pm_add(dev);
kobject_uevent (&dev->kobj, KOBJ_ADD);
bus_attach_device(dev);
if (bus->p->drivers_autoprobe)
ret = device_attach(dev);
klist_add_tail(&dev->knode_parent, &parent->klist_children);
list_add_tail(&dev->node, &dev->class->p->class_devices);
list_for_each_entry(class_intf, &dev->class->p->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
3.3 device 的四種類型
在 sysfs 的裝置模型中,有四種裝置類型:
實體裝置 有 parent 裝置,沒有 class
直接虛拟裝置 有 parent 裝置和 class , parent 沒有 class
間接虛拟裝置 有 parent 裝置和 class , parent 有 class
純虛拟裝置 沒有 parent ,有 class (網絡環回裝置等)
以挂在 PCI 總線上的 I2C 擴充卡為例,首先需要建立一個裝置,使其 bus 域指向 PCI bus ,這是一個實體裝置;然後,以這個實體裝置為父裝置,建立一個 class 為 I2C_adapter_class 的子裝置,這個裝置是直接虛拟裝置,描述 I2C adapter 的功能。 I2C 子系統對每一個 I2C adapter ,又進一步建立了一個字元裝置, I2C dev ,這個字元裝置的 class 被設定為 I2C_device_class ,這裡 I2C dev 就是一個間接虛拟裝置。
除非是純虛裝置,否則任何一個虛拟裝置向父裝置追溯,一定能找到一個實體裝置。
struct device 中有兩個域, bus 和 class ,這兩個域不能同時有值。 如果 bus 域非空,說明這個 struct device 是挂在某個總線上,那麼它必須是一個實體裝置, class 域必須是 NULL ;如果 class 域非空,說明這是一個屬于某個類的虛拟裝置,那麼它的 bus 域就必須是 NULL 。是以,在上節提到的兩個函數, device_add_class_symlinks(dev) 與 bus_add_device(dev) 中,雖然都建立了 subsystem 連結,但它們隻有一個會起作用,否則系統會崩潰。
setup_parent 函數
void setup_parent(struct device *dev, struct device *parent) ;
這個函數用來決定 dev 被加到 sysfs 的哪個目錄下。代碼邏輯為:
kobj = get_device_parent(dev, parent);
if (kobj)
dev->kobj.parent = kobj;
static struct kobject *get_device_parent(struct device *dev, struct device *parent) ;
這個函數會按照裝置類型決定裝置的父目錄 :
l 如果是實體裝置且有父裝置 ( dev->class == NULL && dev->parent )
父目錄就是父裝置代表的目錄
l 如果是直接虛拟裝置 ( dev->class && dev->parent && dev->parent->class != NULL )
在父裝置代表的目錄下建立一個子目錄,名字為 dev->class->name 。然後把這個建立的目錄作為裝置的父目錄 :/sys/devices/<parent_name >/<class_name> 。
l 如果是間接虛拟裝置 ( dev->class && dev->parent && dev->parent->class == NULL )
父目錄就是父裝置代表的目錄
l 如果是純虛拟裝置 ( dev->class && dev->parent == NULL )
父目錄為 /sys/devices/virtual/<class_name>
l 如果是實體裝置且沒有父裝置 ( dev->class == NULL && dev->parent == NULL )
本函數不設定父目錄,傳回 NULL 。但由于此函數傳回後會繼續調用 kobject_add , 是以父目錄會設定成 dev->kobj->kset 代表的目錄 , 也就是一開始 device_initialize 函數裡 設定的 /sys/devices 目錄。可以看出這是實體 root 裝置,比如 platform ( /sys/devices/platform )。
4. Driver
4.1 struct device_driver 基本結構
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
struct attribute_group **groups;
struct pm_ops *pm;
struct driver_private *p;
};
struct driver_private {
struct kobject kobj;
struct klist klist_devices;
struct klist_node knode_bus;
struct module_kobject *mkobj;
struct device_driver *driver;
};
4.2 driver_register
driver_register 将 driver 注冊到 sysfs 系統中,在注冊之前需要對 driver 進行初始化
struct device_driver driver = {
.name = <driver name>;
.bus = <bus>;
.probe = <probe func>; // 探測裝置
.remove = <remove func>; // 移除裝置
.suspend = <suspend func>; // 挂起裝置 進入低功耗狀态)
.resume = <resume func>; // 運作裝置(從低功耗狀态恢複)
};
int driver_register(struct device_driver *drv) // 将 driver 登記到 sysfs 系統中
bus_add_driver(drv);
driver_add_groups(drv, drv->groups);
int bus_add_driver(struct device_driver *drv)
// 配置設定并初始化 struct driver_private
。。。
priv->kobj.kset = bus->p->drivers_kset; // 父目錄指向 bus 的 drivers 子目錄
// 為 driver 建立一個 kobj ,父目錄在上一行的 kset 中指定了
kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, “s%”, drv->name);
if (drv->bus->p->drivers_autoprobe)
driver_attach(drv);
// 把 driver 加入到 bus 的 drivers 清單
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
module_add_driver (drv->owner, drv);
driver_create_file(drv, &driver_attr_uevent); // 加入 uevent 屬性
driver_add_attrs(bus, drv); // 将 bus 中 drv_attrs 屬性清單加入 driver 目錄
add_bind_files(drv); // 加入 bind 、 unbind 屬性(與熱插拔有關)
kobject_uevent(&priv->kobj, KOBJ_ADD);
int driver_add_groups(struct device_driver *drv, struct attribute_group **groups)
對 group 中每一個 group ,調用 sysfs_create_group 在 driver 下建立一個子目錄,并将 group 裡的屬性作為檔案加入到子目錄中。
5. sysfs
前面提到的 class 、 bus 、 drivers 和 devices 在 sysfs 系統中都是以目錄表示;它們的屬性則由檔案表示。所有的目錄、檔案都是通過 sysfs 子產品提供的函數建立和維護。這些 sysfs 的函數主要包括:
// 在 kobj 代表的目錄下建立一個檔案
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) ;
sysfs_add_file
sysfs_add_file_mode
// 在 kobj 代表的目錄下建立連結,指向 target ,連結名為 name
int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name);
// 如果 grp->name 存在,在 kobj 代表的子目錄下建立以 grp->name 命名的子目錄
// 在 kobj 代表的目錄或建立的子目錄下添加屬性檔案
int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) ;
// 在 sysfs 中建立子目錄
struct kset *kset_create_and_add(const char *name, struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj) ;
來自:http://blog.csdn.net/walkingman321/archive/2010/09/30/5916693.aspx