Linux内核大讲堂 (一) 设备驱动的基石驱动模型(5)
上节我们已经领教了传说中的bus_register,这节我们继续领教同样是神级的driver_register。
driver_register如果看懂了,device自行分析应该没太大的问题。
照样先给出一个小的例子代码。
typedef struct __wwhs_device_driver{
char *name;
struct device_driver driver;
}wwhs_device_driver;
static wwhs_device_driver wwhs_driver = {
.name= "wwhs_driver",
};
static int wwhs_driver_register(wwhs_device_driver *wwhs_driver)
{
wwhs_driver->driver.name = wwhs_driver->name;
wwhs_driver->driver.bus=&wwhs_bus_type;
return driver_register(&wwhs_driver->driver);
}
就这么几行,在这里给大家布置个小作业,请把这个自行加到上一节我们给出的代码中合适的位置,注意释放哦.^_^
其实主要就是一个函数啦,这个函数不是我写的,是高手写的,高手写的肯定得膜拜一下,我们接下来就一起膜拜一下高手的作品,先给出函数定义:
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
other = driver_find(drv->name, drv->bus);
if (other) {
put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
首先我们可以看到上面
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
这一堆都是吓人的,可以不用理它们。
接下来
other = driver_find(drv->name, drv->bus);
if (other) {
put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
这个其实就是查找之前是否有被注册过,我们现在显然不会先对这王八蛋进行分析,我们要抓重点,节省精力,要不然晚上哪有时间陪MM约会,哪有时间玩游戏,哪有时间看电影…
接下来就是bus_add_driver了:
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
error = driver_add_attrs(bus, drv);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
__func__, drv->name);
}
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
}
kobject_uevent(&priv->kobj, KOBJ_ADD);
return 0;
out_unregister:
kobject_put(&priv->kobj);
kfree(drv->p);
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}
下面这一段:
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
其实就是为我们的帅哥driver同学的私有成员driver_private分配了空间并且做了一些初始化的动作,前面如果仔细看了文章的同志应该具备了自行分析的能力,看懂肯定是没问题的。但是这中间有一个成员是大家都会感兴趣的,这个就是我们device_driver帅哥为device美女准备的一排金丝鸟笼,这个帅哥很花心的,只要合他自已口味的美女都会拉过来放到金丝鸟笼里,说了这么久,这藏美女的鸟笼是哪位呢?君请看:
klist_init(&priv->klist_devices, NULL, NULL);就是它了,只要合乎帅哥口味的美女,全都被帅哥养在这里面。
接下来:
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
记得我们在bus_register中讲到的会创建六个文件吧,在这里我们有一个就派上用场了,这就是drivers_autoprobe,这个文件我们是可以读写的。如果我们往里面写1:
echo “1” > /sys/bus/wwhs_bus/drivers_autoprobe
那么就会调用driver_attach(drv)。
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
在这里要说一下klist,klist是在内核的双向链表的基础上封装出来的,这段代码的意思就是轮询klist_devices链表中的节点并取节点内含的大美女device成员。然后将其与帅哥device_driver一同作为参数传给回调函数fn,而fn就是我们之前传入的__driver_attach()。我们在这里假设bus里面已经有一位或N位美女( 如果一位美女都没有,就会判断直接返回了,也就没有了下面的故事),
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
if (!driver_match_device(drv, dev))
return 0;
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
}
在这个回调函数里,首先会调用driver_match_device(drv, dev):
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
这个函数的作用其实就是:如果总线的match函数存在,则调用总线的match函数,否则直接返回1。在liunx函数的返回值是很有讲究的,大家一定不可忽视了返回值的存在。
接下来就是:
if (dev->parent) /* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
在这段代码中driver_probe_device(drv, dev)是重中之重。
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
pm_runtime_get_noresume(dev);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv);
pm_runtime_put_sync(dev);
return ret;
}
在这个函数中我们又调用了really_probe(dev, drv);
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev->driver = NULL;
</
分享到:
相关推荐
深入linux设备驱动程序内核机制 pdf深入linux设备驱动程序内核机制深入linux设备驱动程序内核机制深入linux设备驱动程序内核机制深入linux设备驱动程序内核机制深入linux设备驱动程序内核机制深入linux设备驱动程序...
linux内核大讲堂_驱动模型_示例代码_无为和尚
浅谈 Linux 内核开发之 PCI 设备驱动
Linux内核驱动开发详解,基于kernel4.0,对驱动工程师很有帮助
linux内核驱动模型linux内核驱动模型linux内核驱动模型
linux 2.6 内核设备驱动模型精华描述,包括链表、hash链表、kobject等
Linux内核和设备驱动程序实验报告
浅谈 Linux 内核开发之 PCI 设备驱动.pdf
Linux驱动开发:Linux内核模块、字符设备驱动、IO模型、设备树、GPIO子系统、中断子系统、platform总线驱动、I2C总线驱动、SPI总线驱动 Linux项目是一个开放源代码的操作系统项目,由林纳斯·托瓦兹(Linus Torvalds...
《Linux设备驱动开发详解-基于最新的Linux4.0内核》相关源码
Linux内核与设备驱动程序学习资料笔记+源码.zipLinux内核与设备驱动程序学习资料笔记+源码.zipLinux内核与设备驱动程序学习资料笔记+源码.zipLinux内核与设备驱动程序学习资料笔记+源码.zipLinux内核与设备驱动程序...
预览合集,请购买正版书籍:全部有书签导引,便于查阅 Linux 设备驱动程序开发 精通 [印] Sreekrishnan Venkaveswaran 高清_.pdf Linux 设备驱动 第三版_En_.pdf ...Linux 设备驱动程序内核机制 深入 陈学松著_.pdf
Linux设备驱动开发详解:基于最新的Linux 4.0内核.pdf Linux设备驱动开发详解:基于最新的Linux 4.0内核.pdf Linux设备驱动开发详解:基于最新的Linux 4.0内核.pdf
Linux内核分析值设备驱动模型,关于设备驱动模型的分析,和内核源代码注释笔记
Linux设备驱动开发详解:基于最新的Linux4.0内核,linux设备驱动开发详解pdf,LINUX源码
深入理解linux内核英文原版+linux设备驱动开发详解深入理解linux内核英文原版+linux设备驱动开发详解深入理解linux内核英文原版+linux设备驱动开发详解
深入剖析Linux内核与设备驱动 完整版 pdf
Linux内核 内核模型Linux内核 Linux内核 内核模型内核模型Linux内核 内核模型
本文深入探讨了Linux设备驱动程序的内核机制,并提供了一个简单的字符设备驱动程序示例。通过源码示例,详细讲解了驱动程序注册与注销、文件操作函数的实现、设备号分配等关键概念和操作方法。 通过学习本文,您将...
深入剖析Linux内核与设备驱动 [董峰编著].part1.rar