linux核心之class介紹(三)
前兩節介紹了class,device結構體的成員,在這一節主要介紹class和device的關鍵函式.
1.classes_init()
系統呼叫路徑:
start_kernel()->>rest_init()->>kernel_init()->>do_basic_setup()->>driver_init()->>classes_init();
函式原始碼:
int __init classes_init(void) { // 建立/sys/class目錄 class_kset = kset_create_and_add("class", NULL, NULL); if (!class_kset) return -ENOMEM; return 0; }
說明:
該函式動態建立一個kset(class_kset ),並把它註冊進系統.會在sysfs檔案系統建立sys/class目錄
2.class_create()
呼叫路徑:
am335x_gpio_init()->>class_create();
驅動模組初始化函式中呼叫.
函式原始碼:
/* This is a #define to keep the compiler from merging different * instances of the __key variable */ #define class_create(owner, name) \ ({ \ static struct lock_class_key __key; \ __class_create(owner, name, &__key); \ }) struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key) { struct class *cls; int retval; // 動態建立class cls = kzalloc(sizeof(*cls), GFP_KERNEL); if (!cls) { retval = -ENOMEM; goto error; } cls->name = name; cls->owner = owner; cls->class_release = class_create_release; // 註冊該class retval = __class_register(cls, key); if (retval) goto error; return cls; error: kfree(cls); return ERR_PTR(retval); } int __class_register(struct class *cls, struct lock_class_key *key) { struct subsys_private *cp; int error; pr_debug("device class '%s': registering\n", cls->name); //動態建立subsys_private結構體 cp = kzalloc(sizeof(*cp), GFP_KERNEL); if (!cp) return -ENOMEM; //初始化subsys_private中的裝置連結串列klist_devices klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put); //初始化subsys_private中的介面連結串列class_interfaces INIT_LIST_HEAD(&cp->class_interfaces); kset_init(&cp->glue_dirs); __mutex_init(&cp->class_mutex, "struct class mutex", key); //設定class名稱為subsys_private.subsys.kobj的名字,用於建立/sys/class/name目錄 error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name); if (error) { kfree(cp); return error; } /* set the default /sys/dev directory for devices of this class */ //設定class.dev_kobj成員指向sysfs_dev_char_kobj,該kobject表示/sys/dev/char目錄 if (!cls->dev_kobj) cls->dev_kobj = sysfs_dev_char_kobj;// sys/dev/char #if defined(CONFIG_BLOCK) /* let the block class directory show up in the root of sysfs */ if (!sysfs_deprecated || cls != &block_class) cp->subsys.kobj.kset = class_kset; //class_kset在classes_init()中建立,表示/sys/class目錄 #else cp->subsys.kobj.kset = class_kset; #endif cp->subsys.kobj.ktype = &class_ktype; cp->class = cls; cls->p = cp; // 設定p指標 // 建立/sys/class/name目錄 error = kset_register(&cp->subsys); if (error) { kfree(cp); return error; } // 建立class自己的屬性檔案class_attrs,這裡好像沒有,呵呵 error = add_class_attrs(class_get(cls)); class_put(cls); return error; }
說明:
1).動態建立class
2).註冊該class
a.動態建立subsys_private結構體
b.初始化subsys_private中的裝置連結串列klist_devices
c.初始化subsys_private中的介面連結串列class_interfaces
d.設定class名稱為subsys_private.subsys.kobj的名字,用於建立/sys/class/name目錄
e.設定class.dev_kobj成員指向sysfs_dev_char_kobj,該kobject表示/sys/dev/char目錄
f.建立/sys/class/name目錄
g.建立class自己的屬性檔案class_attrs,如果有
3.device_create
呼叫路徑:
am335x_gpio_init()->>device_create();
class_dev = device_create(gpio_class,NULL,dev,0,“am335x_gpio”)
驅動模組初始化函式中呼叫.
函式原始碼:
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
}
說明:
1).va_start(),va_end()函式的用法在後面會講解
2).該函式功能解析引數,呼叫device_create_vargs().
struct device *device_create_vargs(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt,
va_list args)
{
struct device *dev = NULL;
int retval = -ENODEV;
if (class == NULL || IS_ERR(class))
goto error;
// 動態建立device
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
retval = -ENOMEM;
goto error;
}
// 初始化device
dev->devt = devt;
dev->class = class;
dev->parent = parent;// NULL
dev->release = device_create_release;
dev_set_drvdata(dev, drvdata); // drvdata = 0
// 設定dev->kobj.name = "am335x_gpio"
retval = kobject_set_name_vargs(&dev->kobj, fmt, args);
if (retval)
goto error;
// 呼叫device_register()
retval = device_register(dev);
if (retval)
goto error;
return dev;
error:
put_device(dev);
return ERR_PTR(retval);
}
說明:
1). 動態建立device
2). 初始化device
3).設定dev->kobj.name = “am335x_gpio”
4).呼叫device_register()
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset; // devices_kset指向sys/devices目錄的kset
kobject_init(&dev->kobj, &device_ktype);//A top-level, abstract class from which other classes are derived.
INIT_LIST_HEAD(&dev->dma_pools);/* dma pools (if dma'ble) */
mutex_init(&dev->mutex);/* mutex to synchronize calls to its driver.*/
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);//Spinlock to protect the resource of the device.
INIT_LIST_HEAD(&dev->devres_head);//The resources list of the device.
device_pm_init(dev);//For device power management.
set_dev_node(dev, -1);// CONFIG_NUMA未定義,所以該函式是空函式
}
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
//增加device的引用計數器
dev = get_device(dev);
if (!dev)
goto done;
//檢測device->p私有資料成員,如果空則需要初始化
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
// 設定dev.kobject.name = dev->init_name
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
// 回讀驗證name是否已經設定
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
//增加device->parent的引用計數器,因為要增加裝置節點
parent = get_device(dev->parent);
//設定dev->kobj.parent = parent,如果未設定parent,則建立到"sys/devices/virtual/class名"目錄裡
setup_parent(dev, parent);
/* use parent numa_node */
if (parent)
set_dev_node(dev, dev_to_node(parent));//CONFIG_NUMA未定義,空函式
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
//建立裝置目錄,如:/sys/devices/virtual/am335x_gpio/am335x_gpio
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error)
goto Error;
/* notify platform of device entry */
//呼叫平臺相關的通知函式
if (platform_notify)
platform_notify(dev);
// 建立uevent屬性檔案
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;
if (MAJOR(dev->devt)) {
// 建立dev屬性檔案
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
//在sys/dev/char目錄建立檔案連線到dev->kobj目錄,如: 251:0 -> ../../devices/virtual/am335x_gpio/am335x_gpio
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
//建立裝置節點,原理還需要再研究???
devtmpfs_create_node(dev);
}
// 建立裝置和class目錄的符號連線,
//1,建立檔名subsystem到class的連線,如:subsystem -> ../../../../class/am335x_gpio
//2.建立class目錄到當前目錄的連線,如:am335x_gpio -> ../../devices/virtual/am335x_gpio/am335x_gpio
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
// 建立裝置相關的屬性檔案
error = device_add_attrs(dev);
if (error)
goto AttrsError;
// 新增匯流排相關的屬性檔案,並加入匯流排的裝置連結串列(這裡沒有新增到bus)
error = bus_add_device(dev);
if (error)
goto BusError;
// 新增電源管理屬性檔案
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
//Add a device to the PM core's list of active devices.
device_pm_add(dev);
/* Notify clients of device addition. This call must come
* after dpm_sysf_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
//通知使用者空間kobject的KOBJ_ADD事件
kobject_uevent(&dev->kobj, KOBJ_ADD);
// probe drivers for a new device
bus_probe_device(dev);
// 新增到裝置樹
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
// 新增裝置到class,並呼叫相應的interfaces
if (dev->class) {
mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->class_mutex);
}
done:
put_device(dev);
return error;
DPMError:
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
devtmpfs_delete_node(dev);
if (MAJOR(dev->devt))
device_remove_sys_dev_entry(dev);
devtattrError:
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
ueventattrError:
device_remove_file(dev, &uevent_attr);
attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
}
說明:
1).增加device的引用計數器
2).檢測device->p私有資料成員,如果空則需要初始化
3).回讀驗證name是否已經設定
4).設定dev->kobj.parent = parent,如果未設定parent,則建立到"sys/devices/virtual/class名"目錄裡
5).建立裝置目錄,如:/sys/devices/virtual/am335x_gpio/am335x_gpio
6).呼叫平臺相關的通知函式
7).建立uevent屬性檔案
8).建立dev屬性檔案
9).在sys/dev/char目錄建立檔案連線到dev->kobj目錄,如: 251:0 -> …/…/devices/virtual/am335x_gpio/am335x_gpio
10).建立裝置節點,原理還需要再研究???
11).建立裝置和class目錄的符號連線
a.建立檔名subsystem到class的連線,如:subsystem -> …/…/…/…/class/am335x_gpio
b.建立class目錄到當前目錄的連線,如:am335x_gpio -> …/…/devices/virtual/am335x_gpio/am335x_gpio
12).建立裝置相關的屬性檔案
13).新增匯流排相關的屬性檔案,並加入匯流排的裝置連結串列(這裡沒有新增到bus)
14).新增電源管理屬性檔案
15).新增device到pm(電源管理)連結串列
16).通知使用者空間kobject的KOBJ_ADD事件
17). 新增到裝置樹
18).新增裝置到class,並呼叫相應的interfaces