my代码跟踪之driver_register

内核代码为Linux Kernel:3.4.x  必备知识ldd3第十四章linux设备模型

driver_register跟踪:红色为主线 蓝色为标注

/**

 *struct device_driver - The basic device driver structure

 *@name:     Name of the device driver.

 *@bus: The bus which the device of thisdriver belongs to.

 *@owner:    The module owner.

 *@mod_name:    Used for built-in modules.

 *@suppress_bind_attrs: Disables bind/unbind via sysfs.

 *@of_match_table: The open firmware table.

 *@probe:    Called to query the existenceof a specific device,

 *           whether this driver can work with it,and bind the driver

 *           to a specific device.

 *@remove:  Called when the device isremoved from the system to

 *           unbind a device from this driver.

 *@shutdown:      Called at shut-down timeto quiesce the device.

 *@suspend: Called to put the device to sleepmode. Usually to a

 *           low power state.

 *@resume:  Called to bring a device fromsleep mode.

 *@groups:   Default attributes that getcreated by the driver core

 *           automatically.

 *@pm:        Power management operations ofthe device which matched

 *           this driver.

 *@p:           Driver core's private data,no one other than the driver

 *           core can touch this.

 *

 *The device driver-model tracks all of the drivers known to the system.

 *The main reason for this tracking is to enable the driver core to match

 * updrivers with new devices. Once drivers are known objects within the

 *system, however, a number of other things become possible. Device drivers

 *can export information and configuration variables that are independent

 * ofany specific device.

 */

struct device_driver {

       constchar              *name;

       structbus_type              *bus;

 

       structmodule         *owner;

       constchar              *mod_name;   /* used for built-in modules */

 

       boolsuppress_bind_attrs;      /* disablesbind/unbind via sysfs */

 

       conststruct of_device_id       *of_match_table;

 

       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);

       conststruct attribute_group **groups;

 

       conststruct dev_pm_ops *pm;

 

       structdriver_private *p;

};

 

/**

 *driver_register - register driver with bus

 *@drv: driver to register

 *

 * Wepass off most of the work to the bus_add_driver() call,

 *since most of the things we have to do deal with the bus

 *structures.

 */

int driver_register(struct device_driver*drv)

{

       intret;

       structdevice_driver *other;

 

       BUG_ON(!drv->bus->p);      //p不存在断言并输出信息;其实这个主要是判断bus是否存在 详见标注一

 

       if((drv->bus->probe && drv->probe) ||

           (drv->bus->remove &&drv->remove) ||

           (drv->bus->shutdown &&drv->shutdown)) //driverdriver所属busproberemoveshutdown一组存在则提示更新;主要是判断驱动跟驱动的总线是否有冲突的函数注册,给出警告信息

              printk(KERN_WARNING"Driver '%s' needs updating - please use "

                     "bus_typemethods\n", drv->name);

 

       other= driver_find(drv->name, drv->bus);  //bus上以namedriver;存在说明已经注册 退出返回EBUSY详见标注二

       if(other) {

              printk(KERN_ERR"Error: Driver '%s' is already registered, "

                     "aborting...\n",drv->name);

              return-EBUSY;

       }

 

       ret= bus_add_driver(drv);    //bus添加driver 成功返回0

       if(ret)

              returnret;

       ret= driver_add_groups(drv, drv->groups); //如果grop不为空的话,将在驱动文件夹下创建以group名字的子文件夹,然后在子文件夹下添加group的属性文件

       if(ret)

              bus_remove_driver(drv);       //出错从bus上删除driver

       returnret;

}

标注一:

BUG_ON原型为

#define BUG_ON(condition) do { if(unlikely(condition)) BUG(); } while(0)

#define BUG()do { \

       printk("kernelBUG at %s:%d!\n", __FILE__, __LINE__); \

       panic("BUG!");\

} while (0)

标注一end

标注二:

struct device_driver *driver_find(constchar *name, struct bus_type *bus)

{

       structkobject *k = kset_find_obj(bus->p->drivers_kset, name); //bus的驱动集合里寻找同名的驱动

       structdriver_private *priv;

 

       if(k) {

              /*Drop reference added by kset_find_obj() */

              kobject_put(k);      //如果找到减小引用计数

              priv= to_driver(k);              //通过kobject转换成driver_private,返回相应的驱动

              returnpriv->driver;

       }

       returnNULL;

}

标注二end

int bus_add_driver(structdevice_driver *drv)

{

       structbus_type *bus;

       structdriver_private *priv;

       interror = 0;

 

       bus= bus_get(drv->bus);      //找到该drv所属的bus,其实就是增加该bus->p->subsys->kobject->kref的引用计数

       if(!bus)

              return-EINVAL;

 

       pr_debug("bus:'%s': add driver %s\n", bus->name, drv->name);

 

       priv= kzalloc(sizeof(*priv), GFP_KERNEL);     //分配driver_private结构

       if(!priv) {

              error= -ENOMEM;

              gotoout_put_bus;

       }

       klist_init(&priv->klist_devices,NULL, NULL);  //初始化priv->klist_devices

       priv->driver= drv; //将该drv赋值给priv->driver

       drv->p= priv; //drvdrv->p又等于priv

       priv->kobj.kset= bus->p->drivers_kset;      //指向busdrvier容器

       error= kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,

                                 "%s", drv->name);      //驱动的kobject初始化和添加dirsysfs中,详见标注三

       if(error)

              gotoout_unregister;

 

       klist_add_tail(&priv->knode_bus,&bus->p->klist_drivers);              //初始化busnode链表并添加klist_driver到末尾

       if(drv->bus->p->drivers_autoprobe) {        //drivers_autoprobe其值为1bus注册的时候设定

              error= driver_attach(drv);

              if(error)

                     gotoout_unregister;

       }

       module_add_driver(drv->owner,drv);  //添加drvmodule 标注六

 

       error= driver_create_file(drv, &driver_attr_uevent);  //sysfs的目录下创建文件uevent属性文件,见标注七

       if(error) {

              printk(KERN_ERR"%s: uevent attr (%s) failed\n",

                     __func__,drv->name);

       }

       error= driver_add_attrs(bus, drv);      //driver添加bus上的所有属性

       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);         //添加绑定文件,driver_attr_bind driver_attr_unbind见标注七

              if(error) {

                     /*Ditto */

                     printk(KERN_ERR"%s: add_bind_files(%s) failed\n",

                            __func__,drv->name);

              }

       }

 

       kobject_uevent(&priv->kobj,KOBJ_ADD);              //产生一个KOBJ_ADD uevent

       return0;

 

out_unregister:

       kobject_put(&priv->kobj);

       kfree(drv->p);

       drv->p= NULL;

out_put_bus:

       bus_put(bus);

       returnerror;

}

标注三:

int kobject_init_and_add(struct kobject*kobj, struct kobj_type *ktype,

                      struct kobject *parent, const char *fmt, ...)

{

       va_listargs;

       intretval;

 

       kobject_init(kobj,ktype);      //初始化kobject

 

       va_start(args,fmt);        //动态可变参数的使用

       retval= kobject_add_varg(kobj, parent, fmt, args);     //函数包含的操作kobject_set_name_vargs(kobj, fmt, vargs)设置kobject kobject_add_internal(kobj)

       va_end(args);

 

       returnretval;

}

static intkobject_add_varg(struct kobject *kobj, struct kobject *parent,

                         constchar *fmt, va_list vargs)

{

       intretval;

 

       retval= kobject_set_name_vargs(kobj, fmt, vargs);     //主要是将vargs按照fmt格式给kobject起个名字,从调用关系知道vargsdrv->name,也就是驱动的名字

       if(retval) {

              printk(KERN_ERR"kobject: can not set name properly!\n");

              returnretval;

       }

       kobj->parent= parent;

       returnkobject_add_internal(kobj);

}

static intkobject_add_internal(struct kobject *kobj)    //这个函数主要设置drvierkobjectbus之间的层次关系,然后在sysfs中建立该驱动的文件夹

{

       interror = 0;

       structkobject *parent;

 

       if(!kobj)

              return-ENOENT;

 

       if(!kobj->name || !kobj->name[0]) {

              WARN(1,"kobject: (%p): attempted to be registered with empty "

                      "name!\n", kobj);

              return-EINVAL;

       }

 

       parent= kobject_get(kobj->parent);     //得到父节点,从上面知道parentNULL

 

       /*join kset if set, use it as parent if we do not already have one */

       if(kobj->kset) {     //kset不为空

              if(!parent)     //parent为空

                     parent= kobject_get(&kobj->kset->kobj);

              kobj_kset_join(kobj);

              kobj->parent= parent;

/*如果kset不为空,而parent为空(这里这个条件一定成立的,因为kset=bus->p->drivers_ksetparent=NULL)
则该kobj->parent指向kobj->kset->kobj,而且将kobj加入到kobj->ksetlist中,也就是driver放入buskset列表中,也就是busdriver的容器,实际上bus同时还是device的容器,当然bus本身实质上也是个kobject,所以理解kset这个容器的概念至关重要,它是构成了sysfs的层次结构关系*/

       }

 

       pr_debug("kobject:'%s' (%p): %s: parent: '%s', set: '%s'\n",

               kobject_name(kobj), kobj, __func__,

               parent ? kobject_name(parent) :"<NULL>",

               kobj->kset ?kobject_name(&kobj->kset->kobj) : "<NULL>");

 

       error= create_dir(kobj);              //建立该驱动的文件夹

       if(error) {

              kobj_kset_leave(kobj);

              kobject_put(parent);

              kobj->parent= NULL;

 

              /*be noisy on error issues */

              if(error == -EEXIST)

                     WARN(1,"%s failed for %s with "

                          "-EEXIST, don't try to registerthings with "

                          "the same name in the samedirectory.\n",

                          __func__, kobject_name(kobj));

              else

                     WARN(1,"%s failed for %s (error: %d parent: %s)\n",

                          __func__, kobject_name(kobj), error,

                          parent ? kobject_name(parent) :"'none'");

       }else

              kobj->state_in_sysfs= 1;

 

       returnerror;

}

标注三end

int driver_attach(structdevice_driver *drv)

{

       returnbus_for_each_dev(drv->bus, NULL, drv,__driver_attach);

}

intbus_for_each_dev(struct bus_type *bus, struct device *start,

                   void *data,int (*fn)(struct device *, void *))

{

       structklist_iter i;

       structdevice *dev;

       interror = 0;

 

       if(!bus)

              return-EINVAL;

 

       klist_iter_init_node(&bus->p->klist_devices,&i,

                          (start ? &start->p->knode_bus :NULL));   //bus中的已注册的device列表放到迭代器中,方便索引

       while((dev = next_device(&i)) && !error)  //将驱动逐个地与列表中每一个的device匹配,可能一个驱动匹配好几个设备

              error= fn(dev, data);     //这个fn就是上面传下来的__driver_attach

       klist_iter_exit(&i);

       returnerror;

}

static int__driver_attach(struct device *dev, void *data)

{

       structdevice_driver *drv = data;

 

       /*

        * Lock device and try to bind to it. We dropthe error

        * here and always return 0, because we need tokeep trying

        * to bind to devices and some drivers willreturn an error

        * simply if it didn't support the device.

        *

        * driver_probe_device() will spit a warning ifthere

        * is an error.

        */

 

       if(!driver_match_device(drv, dev))      //driver尝试与device匹配 详见标注四

              return0;

 

       if(dev->parent)     /* Needed for USB */

              device_lock(dev->parent);

       device_lock(dev);

       if(!dev->driver)    //如果设备没有指定driver

              driver_probe_device(drv,dev);     //那么需要初始化匹配到的这个设备attempt to bind device & driver together 详见标注五

       device_unlock(dev);

       if(dev->parent)

              device_unlock(dev->parent);

 

       return0;

}

标注四:

static inline intdriver_match_device(struct device_driver *drv,

                                  struct device *dev)

{

       returndrv->bus->match ? drv->bus->match(dev, drv) : 1;

}

这里看bus的总线的match函数是否已经注册,如果没注册则直接返回1直接成功,如果注册,则调用注册的匹配函数。match通常实现为首先扫描driver支持的id设备表,如果为NULL就用名字进行匹配

i2c总线为例:

structbus_type i2c_bus_type = {
.name = "i2c",
.dev_attrs = i2c_dev_attrs,
.match = i2c_device_match,
...
};
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver = to_i2c_driver(drv);

/* match on an id table if there is one */
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;//
只匹配id的名字和client的名字,跟驱动的名字没有关系,注意这里的client是设备转换过来,而不是设备的本身

return 0;
}
转而调用i2c_match_id();
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
const struct i2c_client *client)
{
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0) //
匹配设备client名字和id_table中的名字
return id;
id++;
}
return NULL;
}
所以i2c总线根据设备client名字和id_table中的名字进行匹配的。如果匹配了,则返回id值,在i2c_device_match中则返回真。也就是busmatch函数将会返回真。那将会进入driver_probe_device()
4-2-1-1-2
driver_probe_device()
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;

if (!device_is_registered(dev)) //
首先判断这个device是否已经注册
return -ENODEV;

pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);

ret = really_probe(dev, drv); //
转而调用really_probe()

return ret;
}

4-2-1-1-2-1,really_probe()

static atomic_t probe_count = ATOMIC_INIT(0); //
记录probe数目
static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue); //probe
队列

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; //
把驱动赋值给dev->drvier
if (driver_sysfs_add(dev)) { //
主要是添加driverdev之间的连接文件,见4-2-1-1-2-1-1分析
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}

if (dev->bus->probe) { //
如果busprobe注册将执行,否则执行driverprobe,这也是函数开始时检测的原因!
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); //driver
绑定dev,见4-2-1-1-2-1-2分析
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;

if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
"%s: probe of %s failed with error %d\n",
drv->name, dev_name(dev), ret);
}
/*
* Ignore errors returned by ->probe so that the next driver can try
* its luck.
*/
ret = 0;
done:
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
return ret;
}
4-2-1-1-2-1-1
driver_sysfs_add
static int driver_sysfs_add(struct device *dev)
{
int ret;

ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
kobject_name(&dev->kobj)); //
driver目录下添加以dev->kobj名字的连接文件,连接到device
if (ret == 0) {
ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
"driver"); //
同样在device目录下添加‘driver’为名字的连接文件连接到drvier
if (ret)
sysfs_remove_link(&dev->driver->p->kobj,
kobject_name(&dev->kobj));
}
return ret;
}
4-2-1-1-2-1-2
driver_bound()
static void driver_bound(struct device *dev)
{
if (klist_node_attached(&dev->p->knode_driver)) { //
查看是否已经绑定
printk(KERN_WARNING "%s: device %s already bound\n",
__func__, kobject_name(&dev->kobj));
return;
}

pr_debug("driver: '%s': %s: bound to device '%s'\n", dev_name(dev),
__func__, dev->driver->name);

if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_BOUND_DRIVER, dev); //
调用注册bus通知链上的所有函数

klist_add_tail(&dev->p->knode_driver,&dev->driver->p->klist_devices); //
将设备的驱动node添加到diverklist_devices.
}

标注四end

标注五:

intdriver_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;

}

static inline int device_is_registered(structdevice *dev)

{

       returndev->kobj.state_in_sysfs;    // sysfs中表示已经注册

}

static int really_probe(structdevice *dev, struct device_driver *drv)

{

       intret = 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;  // devicedriver初始化成该driver

 

       /*If using pinctrl, bind pins now before probing */

       ret= pinctrl_bind_pins(dev);

       if(ret)

              gotoprobe_failed;

 

       if(driver_sysfs_add(dev)) {

              printk(KERN_ERR"%s: driver_sysfs_add(%s) failed\n",

                     __func__,dev_name(dev));

              gotoprobe_failed;

       }

                                                 //利用probe初始化设备

       if(dev->bus->probe) {         //如果busprobe存在就用bus

              ret= dev->bus->probe(dev);

              if(ret)

                     gotoprobe_failed;

       }else if (drv->probe) {         //如果bus的不存在driver的存在再用driver

              ret= drv->probe(dev);

              if(ret)

                     gotoprobe_failed;

       }

 

       driver_bound(dev);        //调用driver_bound进行绑定

       ret= 1;

       pr_debug("bus:'%s': %s: bound device %s to driver %s\n",

               drv->bus->name, __func__, dev_name(dev),drv->name);

       gotodone;

 

probe_failed:

       devres_release_all(dev);

       driver_sysfs_remove(dev);

       dev->driver= NULL;

 

       if(ret == -EPROBE_DEFER) {

              /*Driver requested deferred probing */

              dev_info(dev,"Driver %s requests probe deferral\n", drv->name);

              driver_deferred_probe_add(dev);

       }else if (ret != -ENODEV && ret != -ENXIO) {

              /*driver matched but the probe failed */

              printk(KERN_WARNING

                     "%s: probe of %s failed with error%d\n",

                     drv->name, dev_name(dev), ret);

       }else {

              pr_debug("%s:probe of %s rejects match %d\n",

                     drv->name, dev_name(dev), ret);

       }

       /*

        * Ignore errors returned by ->probe so thatthe next driver can try

        * its luck.

        */

       ret= 0;

done:

       atomic_dec(&probe_count);

       wake_up(&probe_waitqueue);

       returnret;

}

static void driver_bound(structdevice *dev)

{

       if(klist_node_attached(&dev->p->knode_driver)) {    //判断是否已经绑定

              printk(KERN_WARNING"%s: device %s already bound\n",

                     __func__,kobject_name(&dev->kobj));

              return;

       }

 

       pr_debug("driver:'%s': %s: bound to device '%s'\n", dev_name(dev),

               __func__, dev->driver->name);

 

       klist_add_tail(&dev->p->knode_driver,&dev->driver->p->klist_devices);              //将设备添加到driver的链表

 

       /*

        * Make sure the device is no longer in one ofthe deferred lists and

        * kick off retrying all pending devices

        */

       driver_deferred_probe_del(dev);

       driver_deferred_probe_trigger();

 

       if(dev->bus)

              blocking_notifier_call_chain(&dev->bus->p->bus_notifier,

                                        BUS_NOTIFY_BOUND_DRIVER, dev);

}

标注五end

标注六:

void module_add_driver(struct module *mod,struct device_driver *drv)

{

       char*driver_name;

       intno_warn;

       structmodule_kobject *mk = NULL;

 

       if(!drv)

              return;

 

       if(mod)         //一般情况下为THIS_MODULE

              mk= &mod->mkobj;

       elseif (drv->mod_name) {    //如果没模块,则检查驱动的模块名

              structkobject *mkobj;

 

              /*Lookup built-in module entry in /sys/modules */

              mkobj= kset_find_obj(module_kset, drv->mod_name);     //根据驱动模块的名字去module_kset集合中找

              if(mkobj) {

                     mk= container_of(mkobj, struct module_kobject, kobj);    //container_of方法通过kobj转换成module_kobject

                     /*remember our module structure */

                     drv->p->mkobj= mk;    //赋值给驱动的mkobj

                     /*kset_find_obj took a reference */

                     kobject_put(mkobj);

              }

       }

 

       if(!mk)          //mk如果为null则返回

              return;

 

       /*Don't check return codes; these calls are idempotent */

       no_warn= sysfs_create_link(&drv->p->kobj, &mk->kobj,"module");     //在驱动文件夹下创建名为‘module’的链接文件,链接到module文件夹

       driver_name= make_driver_name(drv);      //生成driver_name,module

       if(driver_name) {

              module_create_drivers_dir(mk);   //在具体的module文件夹下创建driver目录

              no_warn= sysfs_create_link(mk->drivers_dir, &drv->p->kobj,

                                       driver_name);        //在上面创建的driver目录下,生成一个名为driver_name指定的链接文件,链接到驱动的文件夹

              kfree(driver_name);

       }

}

static char *make_driver_name(structdevice_driver *drv)  //这个函数的功能就是生成一个名字,这个有bus和驱动的名字组成

{

       char*driver_name;

 

       driver_name= kasprintf(GFP_KERNEL, "%s:%s", drv->bus->name, drv->name);  申请内存并将bus的名字和驱动的名字组成一块,中间加一个冒号

       if(!driver_name)

              returnNULL;

 

       returndriver_name;

}

标注六end

 

标注七:

/**

 *driver_create_file - create sysfs file for driver.

 *@drv: driver.

 *@attr: driver attribute descriptor.

 */

int driver_create_file(struct device_driver*drv,

                     const struct driver_attribute *attr)

{

       interror;

       if(drv)

              error= sysfs_create_file(&drv->p->kobj, &attr->attr);

       else

              error= -EINVAL;

       returnerror;

}

static int driver_add_attrs(struct bus_type*bus, struct device_driver *drv)

{

       interror = 0;

       inti;

 

       if(bus->drv_attrs) {

              for(i = 0; attr_name(bus->drv_attrs[i]); i++) {

                     error= driver_create_file(drv, &bus->drv_attrs[i]);

                     if(error)

                            gotoerr;

              }

       }

done:

       returnerror;

err:

       while(--i >= 0)

              driver_remove_file(drv,&bus->drv_attrs[i]);

       gotodone;

}

static int __must_checkadd_bind_files(struct device_driver *drv)

{

       intret;

 

       ret= driver_create_file(drv, &driver_attr_unbind);

       if(ret == 0) {

              ret= driver_create_file(drv, &driver_attr_bind);

              if(ret)

                     driver_remove_file(drv,&driver_attr_unbind);

       }

       returnret;

}

标注七end

 

 

总结一下,driver的注册,主要涉及将自身挂接到busdriver链表,并将匹配到的设备加入自己的device链表,并且将匹配到的devicedriver成员初始化为该driver,私有属性的driver节点也挂到driver的设备链表下,其中匹配函数是利用利用busmatch函数,该函数通常判断如果driverid表,就查表匹配,如果没有就用driverdevice名字匹配。当匹配成功后如果自动初始化标志允许则调用初始化函数probebusprobe优先级始终高于driver的。另外注意一点driver是没有总的起始端点的,driver不是可具体描述的事物。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

suvine

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值