linux中platform总线解析(一)(platform总线初始化)
2017-11-17 11:35
323 查看
platform初始化的函数调用:
start_kernel-->rest_init-->kernel_init-->kernel_init_freeable-->do_base_setup--->driver_init--->platform_bus_init
初始化函数:
platform初始化函数:
下面就分开解析初始化函数所作的工作。
1, eraly_platform_cleanup函数
早期清理工作,主要是清理early_platform_device_list链表
static __initdata LIST_HEAD(early_platform_driver_list);
static __initdata LIST_HEAD(early_platform_device_list);
2,platform设备的定义
struct device platform_bus = {
.init_name = "platform",
};
可以看到设备只是初始化了名字
3,platform总线的定义
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match, //总线匹配函数(device和driver)
.uevent = platform_uevent, //事件(热插拔)函数
.pm = &platform_dev_pm_ops,//电源管理相关的操作函数
};
在这里对总线的定义主要是设置总线的操作函数
4,platform设备注册函数device_register
对于platform总线就是初始化了操作总线的函数。
/*设备的注册分为两个步骤
*第一步:初始化设备
*第二部:把设备加载到系统中
*/
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
4-1,设备初始化
/*
*注册设备时的i初始化动作
*/
void device_initialize(struct device *dev)
{
//这是设置设备在sysfs文件系统中的显示位置(sys/device目录下)
//这里知道作用就好,后面会再来介绍kset,kobject和sysfs文件系统的相关内容
dev->kobj.kset = devices_kset;
//初始化设备对应的kobj_type的类型,主要时sysfs相关的,包含了对sysfs的操作函数
kobject_init(&dev->kobj, &device_ktype);
//初始化链表(具体作用还没清楚?????????????????????)
INIT_LIST_HEAD(&dev->dma_pools);
//初始化信号量
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
//初始化锁
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
//初始化设备的电源管理相关
device_pm_init(dev);
//在多核操作系统中,设置设备和那个物理核近
set_dev_node(dev, -1);
}
4-2,把设备加入到系统中
/*
*把设备注册进系统中
*这个函数中很多跟sysfs相关的了解就好,后面会仔细分析
*/
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
//如果设备不为空,就增加一次对设备的引用计数
dev = get_device(dev);
if (!dev)
goto done;
//红帽相关的结构体
if (!dev->device_rh)
device_rh_alloc(dev);
//初始化设备的私有化数据
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()
*把设备在sysfs中显示的名字设备为当时初始化的init_name的值
*并把设备的dev->init_name设置为空
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
/*设置设备的名字,不过这里条件不符合,跳过*/
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
//增加父设备的引用计数,并返回父设备的结构体
parent = get_device(dev->parent);
//返回父设备的kobject的结构体,platfrom没有父设备,所以父设备名字是virtual,虚拟设备
kobj = get_device_parent(dev, parent);
if (kobj)
dev->kobj.parent = kobj;
/* use parent numa_node
*根据父设备的numa_node设置本设备的numa_node
*/
if (parent)
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL
* 在sysfs中创建对应的目录
*/
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
//在platform目录下,创建uevent文件
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;
//如果devt>>20大于0,就创建dev这个文件
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
devtmpfs_create_node(dev);
}
//在class中为这个设备创建链接文件(platforms不是设备,不会创建)
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
//为设备创建属性文件
error = device_add_attrs(dev);
if (error)
goto AttrsError;
//把设备加入到设备对应总线的链表中(platformo没有对应的总线)
error = bus_add_device(dev);
if (error)
goto BusError;
//跟电源管理相关(目前不是很了解)
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
//把设备加入到电源管理的链表中
device_pm_add(dev);
/* Notify clients of device addition. This call must come
* after dpm_sysfs_add() and before kobject_uevent().
*/
//把设备加入到设备挂载总线上的通知链表中
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
//为这个刚注册的设备查找一个对应的驱动
bus_probe_device(dev);
//把设备加入到父设备的孩子链表中
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if (dev->class) {
mutex_lock(&dev->class->p->mutex);
//把设备加入到设备对应的class的设备链表中
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->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->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;
}
4-3,获取设备名字dev_name
/*
*获取设备的名字
*/
static inline const char *dev_name(const struct device *dev)
{
/*
*如果由init_name就返回init_name,否则返回kobject中的名字
*/
if (dev->init_name)
return dev->init_name;
return kobject_name(&dev->kobj);
}
4-4,为设备寻找驱动
/*为设备查找驱动
*因为这里的platform没有bus,所以直接返回
*/
void bus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
struct subsys_interface *sif;
int ret;
if (!bus)
return;
//调用device_attach为设备找驱动
if (bus->p->drivers_autoprobe) {
ret = device_attach(dev);
WARN_ON(ret < 0);
}
mutex_lock(&bus->p->mutex);
list_for_each_entry(sif, &bus->p->interfaces, node)
if (sif->add_dev)
sif->add_dev(dev, sif);
mutex_unlock(&bus->p->mutex);
}
[b]这时platform设备就加入到系统中了。
[/b]
5,向系统注册总线
总线注册:
int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
struct lock_class_key *key = &bus->lock_key;
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
//分配subsys_private结构,用于存储设备和驱动
priv->bus = bus;
bus->p = priv;
//初始化bus上的通知连
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
//为subsys设置name
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
//指示属于哪一个子系统
priv->subsys.kobj.kset = bus_kset;
//设置总线对应的sysfs上的操作方法
priv->subsys.kobj.ktype = &bus_ktype;
//设置自动匹配驱动
priv->drivers_autoprobe = 1;
//注册子系统
retval = kset_register(&priv->subsys);
if (retval)
goto out;
//创建总线的uevent文件
retval = bus_create_file(bus, &a
d4fc
mp;bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
//创建devices目录
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
//创建drivers目录
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
INIT_LIST_HEAD(&priv->interfaces);
__mutex_init(&priv->mutex, "subsys mutex", key);
//设置klist_devices对应的操作方法函数
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
//创建bus_attr_drivers_probe和bus_attr_drivers_autoprobe文件
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
//为bus增加默认的属性文件
retval = bus_add_attrs(bus);
if (retval)
goto bus_attrs_fail;
//创建groups的属性文件
retval = bus_add_groups(bus, bus->bus_groups);
if (retval)
goto bus_groups_fail;
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
bus_groups_fail:
bus_remove_attrs(bus);
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
kset_unregister(&bus->p->subsys);
out:
kfree(bus->p);
bus->p = NULL;
return retval;
}
其中使用到的重要的数据结构:
//存储device,driver,bus上关于挂接的设备相关信息
struct subsys_private {
struct kset subsys; //定义在哪个子系统
struct kset *devices_kset; //在这个子系统中的device目录
struct list_head interfaces; //子系统结构
struct mutex mutex;
struct kset *drivers_kset; //子系统中driver目录
struct klist klist_devices; //分配的设备,在bus中指挂接的设备
struct klist klist_drivers; //分配的驱动,在bus中指挂接的驱动
struct blocking_notifier_head bus_notifier; //通知连
unsigned int drivers_autoprobe:1; //是否自动匹配驱动
struct bus_type *bus; //指向所属的bus
struct kset glue_dirs;
struct class *class; //指向所属的class
};
哦啦,这时就把platform总线加入到了系统中了。
其中好多对于sysfs的操作,以及在sysfs中的显示等,在这里只是需要了解就可以了,后面会详细介绍相关的内容。
start_kernel-->rest_init-->kernel_init-->kernel_init_freeable-->do_base_setup--->driver_init--->platform_bus_init
初始化函数:
platform初始化函数:
int __init platform_bus_init(void) { int error; //平台初始化前的一些清理工作 early_platform_cleanup(); //注册platform_bus设备 error = device_register(&platform_bus); if (error) return error; //注册platform_bus_type总线 error = bus_register(&platform_bus_type); if (error) device_unregister(&platform_bus); return error; }
下面就分开解析初始化函数所作的工作。
1, eraly_platform_cleanup函数
早期清理工作,主要是清理early_platform_device_list链表
static __initdata LIST_HEAD(early_platform_driver_list);
static __initdata LIST_HEAD(early_platform_device_list);
void __init early_platform_cleanup(void) { struct platform_device *pd, *pd2; //把early_platform_device_list链表中的元素删除,并把元素占据空间清零 list_for_each_entry_safe(pd, pd2, &early_platform_device_list, dev.devres_head) { list_del(&pd->dev.devres_head); memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head)); } }
2,platform设备的定义
struct device platform_bus = {
.init_name = "platform",
};
可以看到设备只是初始化了名字
3,platform总线的定义
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match, //总线匹配函数(device和driver)
.uevent = platform_uevent, //事件(热插拔)函数
.pm = &platform_dev_pm_ops,//电源管理相关的操作函数
};
在这里对总线的定义主要是设置总线的操作函数
4,platform设备注册函数device_register
对于platform总线就是初始化了操作总线的函数。
/*设备的注册分为两个步骤
*第一步:初始化设备
*第二部:把设备加载到系统中
*/
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
4-1,设备初始化
/*
*注册设备时的i初始化动作
*/
void device_initialize(struct device *dev)
{
//这是设置设备在sysfs文件系统中的显示位置(sys/device目录下)
//这里知道作用就好,后面会再来介绍kset,kobject和sysfs文件系统的相关内容
dev->kobj.kset = devices_kset;
//初始化设备对应的kobj_type的类型,主要时sysfs相关的,包含了对sysfs的操作函数
kobject_init(&dev->kobj, &device_ktype);
//初始化链表(具体作用还没清楚?????????????????????)
INIT_LIST_HEAD(&dev->dma_pools);
//初始化信号量
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
//初始化锁
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
//初始化设备的电源管理相关
device_pm_init(dev);
//在多核操作系统中,设置设备和那个物理核近
set_dev_node(dev, -1);
}
4-2,把设备加入到系统中
/*
*把设备注册进系统中
*这个函数中很多跟sysfs相关的了解就好,后面会仔细分析
*/
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
//如果设备不为空,就增加一次对设备的引用计数
dev = get_device(dev);
if (!dev)
goto done;
//红帽相关的结构体
if (!dev->device_rh)
device_rh_alloc(dev);
//初始化设备的私有化数据
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()
*把设备在sysfs中显示的名字设备为当时初始化的init_name的值
*并把设备的dev->init_name设置为空
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
/*设置设备的名字,不过这里条件不符合,跳过*/
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
//增加父设备的引用计数,并返回父设备的结构体
parent = get_device(dev->parent);
//返回父设备的kobject的结构体,platfrom没有父设备,所以父设备名字是virtual,虚拟设备
kobj = get_device_parent(dev, parent);
if (kobj)
dev->kobj.parent = kobj;
/* use parent numa_node
*根据父设备的numa_node设置本设备的numa_node
*/
if (parent)
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL
* 在sysfs中创建对应的目录
*/
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
//在platform目录下,创建uevent文件
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;
//如果devt>>20大于0,就创建dev这个文件
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
devtmpfs_create_node(dev);
}
//在class中为这个设备创建链接文件(platforms不是设备,不会创建)
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
//为设备创建属性文件
error = device_add_attrs(dev);
if (error)
goto AttrsError;
//把设备加入到设备对应总线的链表中(platformo没有对应的总线)
error = bus_add_device(dev);
if (error)
goto BusError;
//跟电源管理相关(目前不是很了解)
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
//把设备加入到电源管理的链表中
device_pm_add(dev);
/* Notify clients of device addition. This call must come
* after dpm_sysfs_add() and before kobject_uevent().
*/
//把设备加入到设备挂载总线上的通知链表中
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
//为这个刚注册的设备查找一个对应的驱动
bus_probe_device(dev);
//把设备加入到父设备的孩子链表中
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if (dev->class) {
mutex_lock(&dev->class->p->mutex);
//把设备加入到设备对应的class的设备链表中
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->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->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;
}
4-3,获取设备名字dev_name
/*
*获取设备的名字
*/
static inline const char *dev_name(const struct device *dev)
{
/*
*如果由init_name就返回init_name,否则返回kobject中的名字
*/
if (dev->init_name)
return dev->init_name;
return kobject_name(&dev->kobj);
}
4-4,为设备寻找驱动
/*为设备查找驱动
*因为这里的platform没有bus,所以直接返回
*/
void bus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
struct subsys_interface *sif;
int ret;
if (!bus)
return;
//调用device_attach为设备找驱动
if (bus->p->drivers_autoprobe) {
ret = device_attach(dev);
WARN_ON(ret < 0);
}
mutex_lock(&bus->p->mutex);
list_for_each_entry(sif, &bus->p->interfaces, node)
if (sif->add_dev)
sif->add_dev(dev, sif);
mutex_unlock(&bus->p->mutex);
}
[b]这时platform设备就加入到系统中了。
[/b]
5,向系统注册总线
总线注册:
int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
struct lock_class_key *key = &bus->lock_key;
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
//分配subsys_private结构,用于存储设备和驱动
priv->bus = bus;
bus->p = priv;
//初始化bus上的通知连
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
//为subsys设置name
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;
//指示属于哪一个子系统
priv->subsys.kobj.kset = bus_kset;
//设置总线对应的sysfs上的操作方法
priv->subsys.kobj.ktype = &bus_ktype;
//设置自动匹配驱动
priv->drivers_autoprobe = 1;
//注册子系统
retval = kset_register(&priv->subsys);
if (retval)
goto out;
//创建总线的uevent文件
retval = bus_create_file(bus, &a
d4fc
mp;bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
//创建devices目录
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
//创建drivers目录
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}
INIT_LIST_HEAD(&priv->interfaces);
__mutex_init(&priv->mutex, "subsys mutex", key);
//设置klist_devices对应的操作方法函数
klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
//创建bus_attr_drivers_probe和bus_attr_drivers_autoprobe文件
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
//为bus增加默认的属性文件
retval = bus_add_attrs(bus);
if (retval)
goto bus_attrs_fail;
//创建groups的属性文件
retval = bus_add_groups(bus, bus->bus_groups);
if (retval)
goto bus_groups_fail;
pr_debug("bus: '%s': registered\n", bus->name);
return 0;
bus_groups_fail:
bus_remove_attrs(bus);
bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
kset_unregister(&bus->p->subsys);
out:
kfree(bus->p);
bus->p = NULL;
return retval;
}
其中使用到的重要的数据结构:
//存储device,driver,bus上关于挂接的设备相关信息
struct subsys_private {
struct kset subsys; //定义在哪个子系统
struct kset *devices_kset; //在这个子系统中的device目录
struct list_head interfaces; //子系统结构
struct mutex mutex;
struct kset *drivers_kset; //子系统中driver目录
struct klist klist_devices; //分配的设备,在bus中指挂接的设备
struct klist klist_drivers; //分配的驱动,在bus中指挂接的驱动
struct blocking_notifier_head bus_notifier; //通知连
unsigned int drivers_autoprobe:1; //是否自动匹配驱动
struct bus_type *bus; //指向所属的bus
struct kset glue_dirs;
struct class *class; //指向所属的class
};
哦啦,这时就把platform总线加入到了系统中了。
其中好多对于sysfs的操作,以及在sysfs中的显示等,在这里只是需要了解就可以了,后面会详细介绍相关的内容。
相关文章推荐
- linux中platform总线解析(三)(platform驱动的注册)
- linux中platform总线解析(四)(platform设备注册后自动匹配驱动)
- linux中platform总线解析(二)(platform设备的注册)
- Linux下的platform总线驱动(二)
- linux驱动模型开发——linux platform总线机制讲解与实例开发
- Linux驱动程序中的platform总线详解
- Linux下的platform总线驱动代码分析
- 基于S3C2440的嵌入式Linux驱动——DS18B20温度传感器(添加使用platform总线机制)
- Linux Platform Device and Driver的注册过程解析
- linux驱动模型开发——linux platform总线机制讲解与实例开发
- Linux下的platform总线驱动(一)
- linux下platform设备驱动框架的实现与作用解析
- linux驱动模型开发——linux platform总线机制讲解与实例开发
- Linux中__init、__devinit等初始化宏解析和入口函数
- Linux设备模型之platform总线
- linux设备总线驱动模型 之 platform总线驱动
- linux架构下platform总线详解——神文,值得一看
- linux驱动模型开发——linux platform总线机制讲解与实例开发
- Linux 设备驱动开发 — platform设备驱动应用实例解析
- Linux中__init、__devinit等初始化宏解析和入口函数