linux中platform总线解析(二)(platform设备的注册)

来源:互联网 发布:命令者模式 java 编辑:程序博客网 时间:2024/06/07 07:25

这里分析的是linux3.10内核,mips架构。

然后说一下platform设备注册的时机:

那是系统启动到什么时候调用的呢?
就是在device_initcall阶段,也就是对应的对应6阶段。

看一下函数调用:

ls2h_device-initcall--->platform_add_devices(ls2h_platform_devices,ARRAY_SIZE(ls2h_platform_devices))

这里的platform_add_devices进行platfrom设备的注册。

那么这个函数在哪里进行定义的呢?或者说在哪里开始运行的呢?

#define device_initcall(fn)__define_initcall(fn, 6)#define __define_initcall(level,fn) \static initcall_t __initcall_##fn __used \__attribute__((__section__(".initcall" level ".init"))) = fnstatic int __init loongson3_device_init(void){if (loongson_pch)loongson_pch->pch_device_initcall();return 0;}device_initcall(loongson3_device_init);

这里把loongson3_device_init设置为.initcall6.init段中。

而这里loongson3_device_init函数调用的pch_device_initcall是函数ls2h_device_initcall.
也就是在系统初始化的第六阶段开始调用运行。

下面就来看看设备注册都做了什么把。。

int platform_add_devices(struct platform_device **devs, int num){int i, ret = 0;//调用platform_device_register函数设备注册进系统for (i = 0; i < num; i++) {ret = platform_device_register(devs[i]);if (ret) {while (--i >= 0)platform_device_unregister(devs[i]);break;}}return ret;}

调用platform_device_register函数设备注册进系统,也就是函数调用platform_device_register做了下面注册工作

int platform_device_register(struct platform_device *pdev){//初始化设备device_initialize(&pdev->dev);//设备架构相关的数据arch_setup_pdev_archdata(pdev);//调用platform_device_add把设备加入系统中return platform_device_add(pdev);}
void device_initialize(struct device *dev){//设置所属的目录dev->kobj.kset = devices_kset;//在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);//设置跟cpu的距离set_dev_node(dev, -1);}

在这里的arch_setup_pdev_archdata(pdev);为空函数。

int platform_device_add(struct platform_device *pdev){int i, ret;if (!pdev)return -EINVAL;//设置父设备,这里为platfrom设备if (!pdev->dev.parent)pdev->dev.parent = &platform_bus;//设置挂载的总线类型,这里为platform总线pdev->dev.bus = &platform_bus_type;switch (pdev->id) {default://设置设备名字,如有两个网卡设备名字为:mac0,mac1dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);break;case PLATFORM_DEVID_NONE:dev_set_name(&pdev->dev, "%s", pdev->name);break;case PLATFORM_DEVID_AUTO:ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);if (ret < 0)goto err_out;pdev->id = ret;pdev->id_auto = true;dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);break;}//为设备申请资源for (i = 0; i < pdev->num_resources; i++) {struct resource *p, *r = &pdev->resource[i];if (r->name == NULL)r->name = dev_name(&pdev->dev);p = r->parent;if (!p) {if (resource_type(r) == IORESOURCE_MEM)p = &iomem_resource;else if (resource_type(r) == IORESOURCE_IO)p = &ioport_resource;}if (p && insert_resource(p, r)) {dev_err(&pdev->dev, "failed to claim resource %d\n", i);ret = -EBUSY;goto failed;}}//把设备加入到系统中ret = device_add(&pdev->dev);if (ret == 0)return ret; failed:if (pdev->id_auto) {ida_simple_remove(&platform_devid_ida, pdev->id);pdev->id = PLATFORM_DEVID_AUTO;}while (--i >= 0) {struct resource *r = &pdev->resource[i];unsigned long type = resource_type(r);if (type == IORESOURCE_MEM || type == IORESOURCE_IO)release_resource(r);} err_out:return ret;}

这里主要就是调用device_add把设备加入到系统中。

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);//设备的私有数据,包括设备孩子链表操作函数的赋值,以及延后probe链表if (!dev->p) {error = device_private_init(dev);if (error)goto done;}/* *设置设备的名字 */if (dev->init_name) {dev_set_name(dev, "%s", dev->init_name);dev->init_name = NULL;}/* subsystems can specify simple device enumeration */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;}//获取父设备parent = get_device(dev->parent);//获取父设备的kobject结构体kobj = get_device_parent(dev, parent);if (kobj)dev->kobj.parent = kobj;/**根据父设备设置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中创建dev对应的目录error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);if (error)goto Error;/** 设置通知链表*/if (platform_notify)platform_notify(dev);//为设备创建uevent属性文件error = device_create_file(dev, &uevent_attr);if (error)goto attrError;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);}//为设备创建链接文件error = device_add_class_symlinks(dev);if (error)goto SymlinkError;error = device_add_attrs(dev);if (error)goto AttrsError;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);//把设备加入到对应的class链表中if (dev->class) {mutex_lock(&dev->class->p->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->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;}

这里主要的事情(先不考虑和sysfs相关内容):
1,为设备匹配对应的驱动
2,把设备加入到父设备的设备链表中(这里是platform设备的链表)
3,把设备加入到class的设备链表中

这样就完成了把设备加入到系统中。

接下来看看设备匹配的过程:


void bus_probe_device(struct device *dev){struct bus_type *bus = dev->bus;struct subsys_interface *sif;int ret;//先获取设备挂载的总线if (!bus)return;//如果总线设置了自动匹配标志就进行匹配(platform在初始化时设置了这个标志位)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);}

匹配函数,主要是对总线上的驱动链表挨个和device进行匹配。

int device_attach(struct device *dev){int ret = 0;device_lock(dev);//如果设备已经有了驱动,就进行绑定,没有的话,就为设备进行查找if (dev->driver) {if (device_is_bound(dev)) {ret = 1;goto out_unlock;}ret = device_bind_driver(dev);if (ret == 0)ret = 1;else {dev->driver = NULL;ret = 0;}} else {//为设备查找驱动ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);pm_request_idle(dev);}out_unlock:device_unlock(dev);return ret;}
//遍历总线上的驱动链表和设备进行匹配int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,     void *data, int (*fn)(struct device_driver *, void *)){struct klist_iter i;struct device_driver *drv;int error = 0;//这里的start为null,也就是把bus的驱动链表放入到i中。这里涉及了mips的原子操作,在本篇最后讲解一下klist_iter_init_node(&bus->p->klist_drivers, &i,     start ? &start->p->knode_bus : NULL);//遍历bus的驱动链表,查找设备的驱动文件,这里的fn函数就是__device_matchwhile ((drv = next_driver(&i)) && !error)error = fn(drv, data);klist_iter_exit(&i);return error;}

接着往下看_device_match函数:

//也就是调用总线上的match函数进行匹配,匹配完成了在调用driver_probe_device进行绑定static int __device_attach(struct device_driver *drv, void *data){            struct device *dev = data;             if (!driver_match_device(drv, dev))                   return 0;             return driver_probe_device(drv, dev);}//调用总线上的match匹配函数,这里指的是platform总线上的match函数static inline int driver_match_device(struct device_driver *drv,                      struct device *dev){    return drv->bus->match ? drv->bus->match(dev, drv) : 1;}

看一下总线的platform_match函数是怎么进行匹配的:

static int platform_match(struct device *dev, struct device_driver *drv){struct platform_device *pdev = to_platform_device(dev);struct platform_driver *pdrv = to_platform_driver(drv);//这里有4中匹配方式,对应不同的设备,/* Attempt an OF style match first */if (of_driver_match_device(dev, drv))return 1;//ACPI设备的匹配/* Then try ACPI style match */if (acpi_driver_match_device(dev, drv))return 1;//把dev和driver中的id_table对比,如果相同就是匹配了。/* Then try to match against the id table */if (pdrv->id_table)return platform_match_id(pdrv->id_table, pdev) != NULL;//如果名字相同就是匹配了。/* fall-back to driver name match */return (strcmp(pdev->name, drv->name) == 0);}
匹配成功后就会调用函数driver_probe_device进行绑定
int driver_probe_device(struct device_driver *drv, struct device *dev){int ret = 0;//如果要绑定的设备没有进行注册,就报错if (!device_is_registered(dev))return -ENODEV;if (dev->parent)pm_runtime_get_sync(dev->parent);//调用really_probe函数进行绑定pm_runtime_barrier(dev);ret = really_probe(dev, drv);pm_request_idle(dev);if (dev->parent)pm_runtime_put(dev->parent);return ret;}
really_probe函数的内容

static int really_probe(struct device *dev, struct device_driver *drv){int ret = 0;//增加probe_count的计数atomic_inc(&probe_count);//把driver绑定到device上dev->driver = drv;/* If using pinctrl, bind pins now before probing */ret = pinctrl_bind_pins(dev);if (driver_sysfs_add(dev)) {printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",__func__, dev_name(dev));goto probe_failed;}//如果bus上有probe函数就执行bus上的probe函数,如果没有就进行driver中的probe函数//这里对probe函数就不进行展开说明,probe就是设备的初始化等相关操作了。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;}//把device绑定到driver上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;dev_set_drvdata(dev, 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 that the next driver can try * its luck. */ret = 0;done:atomic_dec(&probe_count);wake_up(&probe_waitqueue);return ret;}

这里对于sysfs相关的作为了解,不懂也可以理解本章内容。

这里主要就是把driver绑定到了device上,而调用函数driver_bound来把device绑定到driver上


static void driver_bound(struct device *dev){//查看设备是否已经绑定了驱动if (device_is_bound(dev)) {printk(KERN_WARNING "%s: device %s already bound\n",__func__, kobject_name(&dev->kobj));return;}//把device加入到driver的链表中klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);/* * Make sure the device is no longer in one of the 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);}

此时设备注册就完成了,并匹配了对应的驱动程序。

ok,到这里呢platfoem设备的注册就完成了。


在这个注册过程中正好涉及到mips架构的原子操作,正好了解一下。

看一下使用到的几个结构体:

(这里的结构体比较简单,函数只需要简单看一下其调用过程就好,在调用的最后是原子操作)

typedef struct{int counter;}atomic_t;struct kref {atomic_t refcount;};struct klist_iter {struct klist*i_klist;struct klist_node*i_cur;};struct klist_node {void*n_klist;/* never access directly */struct list_headn_node;struct krefn_ref;};

函数调用,只看函数调用的过程就好,最后哪个汇编函数才是要讲解的原子操作函数

void klist_iter_init_node(struct klist *k, struct klist_iter *i,  struct klist_node *n){i->i_klist = k;i->i_cur = NULL;if (n && kref_get_unless_zero(&n->n_ref))i->i_cur = n;}static inline int __must_check kref_get_unless_zero(struct kref *kref){return atomic_add_unless(&kref->refcount, 1, 0);}static inline int atomic_add_unless(atomic_t *v, int a, int u){return __atomic_add_unless(v, a, u) != u;}static __inline__ int __atomic_add_unless(atomic_t *v, int a, int u){int c, old;c = atomic_read(v);for (;;) {if (unlikely(c == (u)))break;//atomic_cmpxchg函数就是原子性的把v+a并存回v中old = atomic_cmpxchg((v), c, c + (a));if (likely(old == c))break;c = old;}return c;}#define atomic_read(v)(*(volatile int *)&(v)->counter)#define atomic_cmpxchg(v, o, n) (cmpxchg(&((v)->counter), (o), (n)))#define cmpxchg(ptr, old, new)__cmpxchg(ptr, old, new, smp_mb__before_llsc(), smp_llsc_mb())

#define __cmpxchg(ptr, old, new, pre_barrier, post_barrier)\({\__typeof__(ptr) __ptr = (ptr);\__typeof__(*(ptr)) __old = (old);\__typeof__(*(ptr)) __new = (new);\__typeof__(*(ptr)) __res = 0;\//这里的pre_barrier,post_barrier就是保证最后写入到内存中\pre_barrier;\//根据ptr的长度选择操作时的指令是32位还是64位switch (sizeof(*(__ptr))) {\case 4:\__res = __cmpxchg_asm("ll", "sc", __ptr, __old, __new); \break;\case 8:\if (sizeof(long) == 8) {\__res = __cmpxchg_asm("lld", "scd", __ptr,\   __old, __new);\break;\}\default:\__cmpxchg_called_with_bad_pointer();\break;\}\\post_barrier;\\__res;\})

ok,这里就是原子操作啦:

//函数经过删减,只留下了运行的部分。#define __cmpxchg_asm(ld, st, m, old, new)\({\__typeof(*(m)) __ret;\if (kernel_uses_llsc && LOONGSON_LLSC_WAR) {\__asm__ __volatile__(\".setpush\n"\".setnoat\n"\".setmips3\n"\"1:# __cmpxchg_asm \n"\__WEAK_LLSC_MB\"" ld "%0, %2   #把*m放入__ret中\n"\"bne%0, %z3, 2f#如果__ret!=*m,跳转到2 \n"\".setmips0  \n"\"move$1, %z4 #把new放入寄存器$1中\n"\".setmips3\n"\"" st "$1, %1 #把$1的值放入*m中\n"\"beqz$1, 1b #如果$1等于0,跳转到标号1\n"\".setpop\n"\"2:\n"\__WEAK_LLSC_MB\输出: "=&r" (__ret), "=R" (*m)\输入: "R" (*m), "Jr" (old), "Jr" (new)\使用到的地方: "memory");\__ret;\})
这个嵌入式汇编的格式讲解:
$1 就是$1寄存器
%0 是__ret
%1 是输出中的*m
%2 是输入中的*m
%3 是old
%4 是new

最终的动作简单点就是利用ll/sc指令把new中值放到了*m中。

mips架构在硬件上使用ll/sc指令来对原子操作进行支持。








原创粉丝点击