------------------------------------------
本文系本站原創,歡迎轉載!
轉載請註明出處:amingriyue.blog.chinaunix.net
------------------------------------------
/**
* device_register - register a device with the system.
* @dev: pointer to the device structure
*
* This happens in two clean steps - initialize the device
* and add it to the system. The two steps can be called
* separately, but this is the easiest and most common.
* I.e. you should only call the two helpers separately if
* have a clearly defined need to use and refcount the device
* before it is added to the hierarchy.
*
* NOTE: _Never_ directly free @dev after calling this function, even
* if it returned an error! Always use put_device() to give up the
* reference initialized in this function instead.
*/
int device_register(struct device *dev)
{
device_initialize(dev); //初始化dev,見第1部分分析
return device_add(dev); //添加設備,這是device_register的主要工作,見第2部分分析
}
函數定義很簡潔,第一步初始化dev,第二步添加設備。我們也分兩部分進行分析:
1,device_initialize()
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset; //devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL),即devices_kset代表了/sys/devices/
kobject_init(&dev->kobj, &device_ktype); //初始化dev的kobj,這個函數在driver_register中分析過了
INIT_LIST_HEAD(&dev->dma_pools); //初始化dev的內存池的列表
init_MUTEX(&dev->sem); //初始化信號量
spin_lock_init(&dev->devres_lock); //初始化自旋鎖
INIT_LIST_HEAD(&dev->devres_head); //初始化隊列頭
device_init_wakeup(dev, 0); //由其定義知初始化dev->power.can_wakeup = dev->power.should_wakeup = 0;
device_pm_init(dev); //初始化dev->power.status = DPM_ON;
set_dev_node(dev, -1); //如果配置了numa,設置dev->numa_node = -1; numa是非統一內存訪問架構,一般用於服務器中,所以一般嵌入式中不使用。
}
這個dev的初始化主要是設置dev結構中的各個變量等以及對kobj的相關操作。
2,device_add()
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev); //主要是增加dev->kobj->kref的引用
if (!dev)
goto done;
dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL); //這裏p是dev中的device_private結構,跟驅動的driver_private相似
if (!dev->p) {
error = -ENOMEM;
goto done;
}
dev->p->device = dev; //將dev賦值給p的device保存
klist_init(&dev->p->klist_children, klist_children_get,
klist_children_put); //初始化p的klist_children
/*
* 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()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
if (!dev_name(dev))
goto name_error;
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent); //得到父設備
setup_parent(dev, parent);
/* use parent numa_node */
if (parent)
set_dev_node(dev, dev_to_node(parent)); //不使用numa
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); //這個函數在driver_register中分析過了,主要將kobj添加到sys的層次中
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
error = device_create_file(dev, &uevent_attr); //在driver_register中已經分析,主要是在/sys/devices/.../中添加dev的uevent屬性文件
if (error)
goto attrError;
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr); //主要是在sys/devices/...中添加dev屬性文件
if (error)
goto ueventattrError;
error = device_create_sys_dev_entry(dev); //在/sys/dev/char/或者/sys/dev/block/創建devt的屬性的連接文件,形如10:45,由主設備號和次設備號構成,指向/sys/devices/.../的具體設備目錄,該鏈接文件只具備讀屬性,顯示主設備號:次設備號,如10:45,用戶空間udev相應uevent事件時,將根據設備號在/dev下創建節點文件
if (error)
goto devtattrError;
}
error = device_add_class_symlinks(dev); //相互創建dev和class之間的鏈接文件
if (error)
goto SymlinkError;
error = device_add_attrs(dev); //添加設備屬性文件
if (error)
goto AttrsError;
error = bus_add_device(dev); //將設備添加到bus上,創建subsystem鏈接文件,鏈接class下的具體的子系統文件夾
if (error)
goto BusError;
error = dpm_sysfs_add(dev); //添加設備的電源管理屬性,截止這裏,我們的/sys/devices/.../具體設備目錄下至少生成有以下四個屬性文件:uevent,dev,subsystem,power,你找到了嗎?
if (error)
goto DPMError;
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);//執行bus通知鏈上的註冊函數,由設備註冊上來
kobject_uevent(&dev->kobj, KOBJ_ADD); //產生一個KOBJ_ADD的uevent事件,通過netlink機制和用戶空間通信,這個driver_register中已經分析過了
bus_probe_device(dev); //去bus上找dev對應的drv,主要執行__device_attach,主要進行match,sys_add,執行probe函數和綁定等操作
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children); //把設備添加到父設備的children列表中
if (dev->class) { //如果改dev有所屬類,則將dev的添加到類的設備列表裏面
mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->class_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) //執行改dev的class_intf->add_dev(),這個有個好處,就是隻有設備匹配註冊成功了,才進行其它的註冊工作(如字符設備的註冊,生成/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))
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;
}
這部分是device_register的主要工作,我們這裏就不在詳細分析了,相信有了driver_register分析的基礎,這部分分析工作應該會變得很輕鬆~