Nouveau源码分析(四)
probe函数成功返回之后,DRM模块就会调用struct drm_driver的load函数,对应nouveau的nouveau_drm_load.
这个函数虽然看起来不是特别长,但每一个调用的函数展开后就会变得非常长了!
第371行,创建一个nouveau_drm结构体. 这里,我们重新梳理一下nouveau中结构体的线索.
第一条线索 struct nouveau_xxx,这个我们在上一篇中已经见到了很多了,比如nouveau_device nouveau_client nouveau_object [以下简称nv结构体/nv对象]
另一条线索 struct nvif_xxx/struct nouveau_cli,这个我们从这一节会碰到,比如nouveau_cli nvif_client nvif_object nvif_device[以下简称nvif结构体/nvif对象]
每一个nvif对象都会有对应一个nv对象,可以通过ioctl来访问. 比如对一个nvif对象执行ioctl_rd32,就可以执行对应nv对象oclass里的rd32方法.
仔细阅读下面这个图片:
方块里的是类型名称,箭头上的是成员名称 [带*号的表示指针].
我们现在要创建的就是处于最顶层的nouveau_drm,可以看到如果要和上一节创建的nouveau_device对应起来,还有一段路要走.
因为nouveau_drm这个结构体成员包含的面很广,从物理显存、虚拟映射显存,到channel,dma,以及硬件加速移动显存,还有显示模式切换都会有所涉及,所以在此处先不分析这个结构体的每一个成员,先跟着代码走下去吧.
因为struct nouveau_drm第一个成员就是struct nouveau_cli client; 所以可以把nouveau_cli看作是它的base,因此,第371行创建一个struct nouveau_cli,大小是sizeof(struct nouveau_drm) .
第106行,kzalloc分配内存.
第108行,因为nouveau_cli的base是nvif_client,所以这里调用nvif_client的init函数.
第112行,如果nvif_client初始化成功的话,就初始化一个mutex .
第113行,初始化usif. 这是个什么东西呢? 其实就是nvif对user空间的一个接口. 让运行在用户态下的程序能创建usif对象,然后进行读写什么的.
这个初始化usif的函数很简单,就是初始化两个链表.
再来看刚才的nvif_client_init函数:
第76行,首先初始化nvif_object:
首先各种填充字段:
这里的oclass是创建nouveau_object的时候用的.
这里的handle是nouveau_object和nvif_object一一对应用的.
然后dtor是析构函数指针,object是nvif_object()用的,其实就是nvif对象间的转换.
前面说过,每一个nvif_object会对应一个nouveau_object,但对于这个例子来说,创建nouveau_object的代码实际上在第254行,由于object->parent为0,根本执行不到,所以并不会使用这个oclass和handle. [当然,这个nvif_object还是有对应的nouveau_object对象的,只不过不在这里创建了,等会儿会讲到.]
那个if语句的代码也暂时不展开,等到以后真正执行到再回来说. 于是这个函数就这么返回了.
先回到nvif_client_init,下面还有一个循环语句,遍历nvif_drivers:
很明显这是内核里的代码,因此只存在一个nvif_driver,那就是nvif_driver_nvkm.
再看看这个for循环,很明显我们将会调用nvif_driver_nvkm.init():
首先创建一个nouveau_client,这个其实就是一个nouveau_object,也就是说在这里创建了这个nvif_object对应的nouveau_object .
第121行,一个有关notify的函数. notify貌似是一个回调工具,创建一个里面包含函数指针,数据什么的;当发生某个条件后,可以触发这个notify,然后就会调用里面的函数.
再来看nouveau_client_create:
又一次见到了这个东西,直接去看nouveau_client_create_
第211行,首先寻找对应的nouveau_device.
第215行,创建namedb,通过看最开始的结构体,应该也能想象出是个什么东西:
先创建一个nouveau_parent,这个结构体和nouveau_engine都能把u32 oclass转换成对应的nouveau_oclass *oclass .
接下来初始化namedb的list链表,这个链表和nouveau_handle的node链表相连,表示储存在namedb里的nouveau_handle对象 .
首先创建一个nouveau_object对象.
然后下面这个while语句,把sclass连成了一个链表放在了object->sclass中.
然后engcls表示可以用来完成u32 oclass转换的engines.
如果要通过一个nouveau_parent对象来完成u32 oclass的转换,会先从object->sclass中查找.
如果没找到,那么就查看object->engine中指示可用的engine,再从这些engine中查找.
此处的sclass实际上是0,所以object->sclass为0. 而且因为这个结构体实际上是nouveau_client,转换u32 oclass的时候还有一个特别的处理,会直接使用client->device,也就是nouveau_device,到时候再具体说.
回到nouveau_client_create_,接下来创建一个nouveau_handle *root.
首先把parent向上查找,直到找到一个nouveau_namedb,对于这个例子,parent本来就是一个nouveau_namedb,所以parent == namedb.
接着初始化handle的一些字段,紧接着使用nouveau_namedb_insert把这个插入到namedb中. handle的node链表和namedb的list链表相连.
这个insert函数首先查找是否有重复的,有就返回-EEXIST,然后用链表连起来,返回.
第127行,检查parent有没有一个关联handle和object的函数,对于这个例子,内存用kzalloc分配,之后也没有对这个进行初始化. 所以不会进入这个if语句.
第137行,如果object和namedb不一样,那么寻找_parent对应的handle,然后把当前handle的head链表加入到parent handle的tree链表中.
对于这个例子object和namedb相等,因此不会执行进去.
这个地方可以顺便看一下nouveau_namedb_get和nouveau_namedb_put:
代码都很容易理解,自己尝试阅读一下,不多说了. 回到刚才那个函数:
第149行,把phandle赋值为handle,返回.
回到nouveau_client_create_,第229行,初始化几个字段,比如把device字段储存上对应的nouveau_device,这个也就是我们在上一篇中用nouveau_device_create创建的那个.
返回! 至此我们终于可以回到nouveau_drm_load了!
第376行,把新创建的nouveau_drm放进drm提供的结构体里.
第377行,把drm的结构体放进nouveau_drm里.
第378行,一个debug配置信息. 这里的nvkm_xxx就是获得nvif_object所对应的nouveau_object,并把它转换成nouveau_xxx .
第381行,初始化clients链表. 对nouveau设备文件进行open操作时就会创建一个nouveau_cli并加入这个链表. 里面包含的是每个文件特有的东西,比如前面提到的usif .
第384行,获取hdmi设备. 不知道hdmi是什么的可以去百度谷歌一下.
第341行,检查是否是PCI设备.
第348行,使用PCI模块的函数获取HDMI对应的PCI设备.
第356行,检查获取的HDMI设备的class信息.
接下来的nvif_device_init又是一个大函数,暂且先到这. 下一篇再讨论nvif_device_init函数.
仔细阅读下面这个图片:
方块里的是类型名称,箭头上的是成员名称 [带*号的表示指针].
我们现在要创建的就是处于最顶层的nouveau_drm,可以看到如果要和上一节创建的nouveau_device对应起来,还有一段路要走.
因为nouveau_drm这个结构体成员包含的面很广,从物理显存、虚拟映射显存,到channel,dma,以及硬件加速移动显存,还有显示模式切换都会有所涉及,所以在此处先不分析这个结构体的每一个成员,先跟着代码走下去吧.
因为struct nouveau_drm第一个成员就是struct nouveau_cli client; 所以可以把nouveau_cli看作是它的base,因此,第371行创建一个struct nouveau_cli,大小是sizeof(struct nouveau_drm) .
第106行,kzalloc分配内存.
第108行,因为nouveau_cli的base是nvif_client,所以这里调用nvif_client的init函数.
第112行,如果nvif_client初始化成功的话,就初始化一个mutex .
第113行,初始化usif. 这是个什么东西呢? 其实就是nvif对user空间的一个接口. 让运行在用户态下的程序能创建usif对象,然后进行读写什么的.
这个初始化usif的函数很简单,就是初始化两个链表.
再来看刚才的nvif_client_init函数:
第76行,首先初始化nvif_object:
首先各种填充字段:
这里的oclass是创建nouveau_object的时候用的.
这里的handle是nouveau_object和nvif_object一一对应用的.
然后dtor是析构函数指针,object是nvif_object()用的,其实就是nvif对象间的转换.
前面说过,每一个nvif_object会对应一个nouveau_object,但对于这个例子来说,创建nouveau_object的代码实际上在第254行,由于object->parent为0,根本执行不到,所以并不会使用这个oclass和handle. [当然,这个nvif_object还是有对应的nouveau_object对象的,只不过不在这里创建了,等会儿会讲到.]
那个if语句的代码也暂时不展开,等到以后真正执行到再回来说. 于是这个函数就这么返回了.
先回到nvif_client_init,下面还有一个循环语句,遍历nvif_drivers:
很明显这是内核里的代码,因此只存在一个nvif_driver,那就是nvif_driver_nvkm.
再看看这个for循环,很明显我们将会调用nvif_driver_nvkm.init():
首先创建一个nouveau_client,这个其实就是一个nouveau_object,也就是说在这里创建了这个nvif_object对应的nouveau_object .
第121行,一个有关notify的函数. notify貌似是一个回调工具,创建一个里面包含函数指针,数据什么的;当发生某个条件后,可以触发这个notify,然后就会调用里面的函数.
再来看nouveau_client_create:
又一次见到了这个东西,直接去看nouveau_client_create_
第211行,首先寻找对应的nouveau_device.
第215行,创建namedb,通过看最开始的结构体,应该也能想象出是个什么东西:
先创建一个nouveau_parent,这个结构体和nouveau_engine都能把u32 oclass转换成对应的nouveau_oclass *oclass .
接下来初始化namedb的list链表,这个链表和nouveau_handle的node链表相连,表示储存在namedb里的nouveau_handle对象 .
首先创建一个nouveau_object对象.
然后下面这个while语句,把sclass连成了一个链表放在了object->sclass中.
然后engcls表示可以用来完成u32 oclass转换的engines.
如果要通过一个nouveau_parent对象来完成u32 oclass的转换,会先从object->sclass中查找.
如果没找到,那么就查看object->engine中指示可用的engine,再从这些engine中查找.
此处的sclass实际上是0,所以object->sclass为0. 而且因为这个结构体实际上是nouveau_client,转换u32 oclass的时候还有一个特别的处理,会直接使用client->device,也就是nouveau_device,到时候再具体说.
回到nouveau_client_create_,接下来创建一个nouveau_handle *root.
首先把parent向上查找,直到找到一个nouveau_namedb,对于这个例子,parent本来就是一个nouveau_namedb,所以parent == namedb.
接着初始化handle的一些字段,紧接着使用nouveau_namedb_insert把这个插入到namedb中. handle的node链表和namedb的list链表相连.
这个insert函数首先查找是否有重复的,有就返回-EEXIST,然后用链表连起来,返回.
第127行,检查parent有没有一个关联handle和object的函数,对于这个例子,内存用kzalloc分配,之后也没有对这个进行初始化. 所以不会进入这个if语句.
第137行,如果object和namedb不一样,那么寻找_parent对应的handle,然后把当前handle的head链表加入到parent handle的tree链表中.
对于这个例子object和namedb相等,因此不会执行进去.
这个地方可以顺便看一下nouveau_namedb_get和nouveau_namedb_put:
代码都很容易理解,自己尝试阅读一下,不多说了. 回到刚才那个函数:
第149行,把phandle赋值为handle,返回.
回到nouveau_client_create_,第229行,初始化几个字段,比如把device字段储存上对应的nouveau_device,这个也就是我们在上一篇中用nouveau_device_create创建的那个.
返回! 至此我们终于可以回到nouveau_drm_load了!
第376行,把新创建的nouveau_drm放进drm提供的结构体里.
第377行,把drm的结构体放进nouveau_drm里.
第378行,一个debug配置信息. 这里的nvkm_xxx就是获得nvif_object所对应的nouveau_object,并把它转换成nouveau_xxx .
第381行,初始化clients链表. 对nouveau设备文件进行open操作时就会创建一个nouveau_cli并加入这个链表. 里面包含的是每个文件特有的东西,比如前面提到的usif .
第384行,获取hdmi设备. 不知道hdmi是什么的可以去百度谷歌一下.
第341行,检查是否是PCI设备.
第348行,使用PCI模块的函数获取HDMI对应的PCI设备.
第356行,检查获取的HDMI设备的class信息.
接下来的nvif_device_init又是一个大函数,暂且先到这. 下一篇再讨论nvif_device_init函数.