参考链接

dma:https://blog.csdn.net/abc3240660/article/details/81942190#t6
Linux DRM KMS 驱动简介:https://blog.csdn.net/zhuyong006/article/details/80942777
DRM(Direct Rendering Manager)学习简介:https://blog.csdn.net/hexiaolong2009/article/details/83720940 

 
 

    应用场景

    linux drm(Direct Rendering Manager)设计之初是作为一套display数据传输流程,用于将camera采集的视频数据抛给display显示。
    drm驱动与应用程序之间封装了一个中间层libdrm,应用程序可以通过调用libdrm提供的接口实现对drm驱动的操作,相对简单。

    主要模块

    drm系统主要分为三个模块:libdrm,GEM,KMS。

    libdrm

    libdrm运行在用户空间,是应用程序与内核之间交互的桥梁,其功能主要是填充内核需要的结构并通过ioctl调用传入内核,内核填充后再返回给应用空间。

    GEM

    GEM(Graphic Execution Manager)主要负责buffer的操作。

    KMS

    KMS(Kernel Mode Setting)主要负责相关参数的设置(包括分辨率、刷新率、电源状态(休眠唤醒)等)和显示画面的切换(显示buffer的切换,多图层的合成方式,以及每个图层的显示位置)。

    基本元素

    KMS

      1. CRTC:对显示buffer进行扫描,并产生时序信号的硬件模块,通常指Display Controller;

    a) DPMS (Display Power Manage System) 电源状态管理 (crtc_funcs->dpms)
    b) 将 Framebuffer 转换成标准的 LCDC Timing ,其实就是一帧图像刷新的过程(crtc_funs->mode_set)
    c) 帧切换,即在 VBlank 消影期间,切换 Framebuffer(crtc_funcs->page_flip)

      1. Encoder :负责将CRTC输出的timing时序转换成外部设备所需要

    a) DPMS (Display Power Manage System) 电源状态管理 (encoder_funcs->dpms)
    b) 将 VOP 输出的 lcdc Timing 打包转化为对应接口时序 HDMI TMDS / … (encoder_funcs->mode_set)
    encoder是crtc和connector的连接者

      1. CONNECTOR:连接物理显示设备的连接器,如HDMI、DisplayPort、DSI总线,通常和Encoder 驱动绑定在一起,与display联系最为密切;

    a) 获取和上报display热拔插(Hotplug)状态
    b) 读取并解析屏 (Panel) 的 EDID 信息

      1. PLANE:硬件图层,有的Display硬件支持多层合成显示,但所有的Display Controller至少 要有1个plane;

    a) plane是ctrc和framebuffer的连接者
    b) 每个crtc至少要有一个plane
    c) DRM中的Plane和我们常说的YUV/YCbCr图形格式中的plane完全是两个不同的概念。YUV图形格式中的plane指的是图 像数据在内存中的排列形式,一般Y通道占一段连续的内存块,UV通道占另一段连续的内存块,我们称之为YUV-2plane (也叫YUV 2平面),属于软件层面。而DRM中的Plane指的是Display Controller中用于多层合成的单个硬件图层模块, 属于硬件层面
    d) 随着软件技术的不断更新,对硬件的性能要求越来越高,在满足功能正常使用的前提下,对功耗的要求也越来越苛刻。本来GPU可以 处理所有图形任务,但是由于它运行时的功耗实在太高,设计者们决定将一部分简单的任务交给Display Controller去处理(比如合 成),而让GPU专注于绘图(即渲染)这一主要任务,减轻GPU的负担,从而达到降低功耗提升性能的目的。于是,Plane(硬件图 层单元)就诞生了

      1. FB:Framebuffer即单个图层的显示内容,唯一一个和硬件无关的基本元素

    a) buffer就是一块分配的内存,和硬件没有关系,一般都是通过DMA_BUF机制将该buffer和camera的数据buf关联起来,以达到数据快速转移过来的目的

      1. property:任何你想设置的参数,都可以做成property,是DRM驱动中最灵活、最方便的Mo de setting机制

    a) Atomic机制的引入,可以,减少上层应用接口的维护工作量。当开发者有新的功能需要添加时,无需增加新的函数名和IOCTL,只需在底层驱动中新增 一个property,然后在自己的应用程序中获取/操作该property的值即可
    b) Property的结构简单概括主要由3部分组成:name、id 和 value。其中id为该property在DRM框架中全局唯一的标识符
    c) 增强了参数设置的灵活性。一次IOCTL可以同时设置多个property,减少了userspace与kernel space切换的次数,同时最 大限度的满足了不同硬件对于参数设置的要求,提高了软件效率

    GEM

      1. DUMB:只支持连续物理内存的buffer类型,基于kernel中通用CMA API实现,多用于小分辨率简单场景
      1. PRIME:连续、非连续物理内存都支持,基于DMA-BUF机制,可以实现buffer共享,多用 于大内存复杂场景
      1. fence:buffer同步机制,基于内核dma_fence机制实现,用于防止显示内容出现异步问题

    模块行为

    以 HDMI Monitor 显示的过程为例,实例解析下 CRTC / Encoder / Connector 的行为

      1. 首先 HDMI 驱动检测到电视 Plugin 信号,读出电视的 EDID 信号,获取电视的分辨率信息 (DRM Connector)
      1. Userspace 将需要显示的数据填充在 framebuffer 里面,然后通过 libdrm 接口通知 VOP 设备开始显示
      1. 接着 VOP 驱动将 framebuffer 里面的数据转换成标准的 LCDC Timing 时序 (DRM CRTC)
      1. 同时 HDMI 驱动将 HDMI 硬件模块的 LCDC 时序配置与 VOP 输出时序一致,准备将输入的 LCDC Timing 转化为电视识别的 HDMI TMDS 信号 (DRM Encoder)

    drm应用程序调用流程

      1. 初始化
    open(/dev/dri/card0) //open drm device
    --> drmSetClientCap(DRM_CLIENT_CAP_UNIVERSAL_PLANES) 
    --> drmSetClientCap(DRM_CLIENT_CAP_ATOMIC) 
    --> drmModeGetResources()
    --> drmModeGetConnector() //found connector DSI 
    --> drmModeObjectGetProperties() //found connector dpms prop 
    --> drmModeGetEncoder() //found encoder DSI
    --> drmModeGetCrtc() //found crtc that connect to DSI 
    --> drmGetPlaneByType(DRM_PLANE_TYPE_PRIMARY) //get PRIMARY/OVERLAY type drm plane info
    --> drmModeObjectGetProperties(plane_id,DRM_MODE_OBJECT_PLANE) //get drm plane properties 
    --> drmModeGetProperty() //get drm info like crtc_id,fb_id and so on,they be used for commit
    
     
     

      获取crtc,encoder,connector,plane的先后顺序为connector,encoder,crtc,plane,每个前者的结构中都有后者的id号,发现connector后可以用过其结构下的encoder_id找到与自己连接的encoder模块,同理直到找到从plane到connector的连接通路。

        1. 创建dumb空间并通过mmap映射到应用层
      drmIoctl(DRM_IOCTL_MODE_CREATE_DUMB) //创建dumb buffer(只支持连续物理内存,基于kernel中通用CMA API实现)
      --> drmIoctl(DRM_IOCTL_MODE_MAP_DUMB) //获取dumb buffer的映射偏移值
      --> mmap() //通过mmap映射内核空间到应用层
      --> drmPrimeHandleToFD(fd,handle,0,&fd2) //handle已和fd绑定,在此将fd2与handle绑定,即fd2同fd相同
      --> drmModeAddFB2() //添加framebuffer
      
       
       
          1. 将相机数据传入前面mmap映射的地址中

        可以将相机数据从相机buf拷贝到mmap空间,但该方式效率太低,对于高帧率的应用场景可以使用DMA_BUF机制将相机数据buf和mmap空间建立连接,使数据通过dma通道直达mmap空间。

          1. 提交数据
        drmModeAtomicAlloc //申请Atomic结构
        --> drmModeAtomicAddProperty() //将前面获取的crtc_id,fb_id等参数都传入申请的Atomic结构中
        --> drmModeAtomicCommit() //提交数据到display
        
         
         
            1. 资源释放
          drmModeRmFB(fb_id) //删除drmModeAddFB2()添加的framebuffer,不然会造成shmem泄露
          --> munmap() //释放mmap映射的内存
          --> close() //关闭打开的drm句柄
          
           
           

            以上便为应用程序调用libdrm的流程,其中的3和4在循环中,1和2只需要调用一次,但在某些场景中需要释放2中申请的资源,但1中初始化的不能释放;
            该场景为:单一plane,crtc等,需要多次打开和关闭display,且系统中有多个功能块在使用drm中的一个或多个模块(比如视频编码用到encoder模块),这样,若将1中初始化的资源释放掉,再次获取时可能会被其他进程占用导致无法获取,这种场景下就需要保留1中资源,但多次申请/是否2中资源。
            但这样会引入一个问题:调用drmModeRmFB函数释放fb,但若释放了正在用于提交的fb,内核会将crtc关闭,导致vop数据传输通路断裂,而且没有framebuffer时Encoder也会被disable,此时再打开vop也无法获取Encoder,致使vop不通;虽然内核宏CONFIG_DRM_FBDEV_EMULATION使能时,在所有的drmfd关闭后fbdev会重新配置crtc,但由于其他进程也打开了drm,因此crtc不会再次被设置,除非重启程序。
            解决这一问题的方法是:打开vop时调用drmModeSetCrtc函数手动设置crtc,即使无法找到Encoder,该函数也会使用一个默认的Encoder将crtc,encoder,connector三者建立连接,从而打通vop通路。
            也就是说在以上的应用场景下即使内核默认使能了宏CONFIG_DRM_FBDEV_EMULATION,也需要调用到drmModeSetCrtc()函数,非以上场景可不用调用(对于Atomic模式来说)。

            libdrm与内核的交互

              1. 创建dumb buffer

            该操作使用idr机制将资源与一个id号关联起来,由此可以使用一个整数标识整个资源,非常简洁,但是一个整数能够记录表达的信息太有限了,所以整数 ID 的背后常常都有一个结构体与之对应;

            drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &alloc_arg); //struct drm_mode_create_dumb alloc_arg,该函数会填充该结构下的用于标识资源的id号handle
                -->ioctl(fd,DRM_IOCTL_MODE_CREATE_DUMB,alloc_arg) //通过ioctl与内核交互
            内核:
            drm_mode_create_dumb_ioctl()
                -->rockchip_gem_dumb_create(struct drm_mode_create_dumb *args)
                    -->rockchip_gem_create_with_handle(&args->handle)
                        -->rockchip_gem_create_object() //创建struct drm_gem_object结构指针*obj
                        -->drm_gem_handle_create(obj,handle) //申请一个id(使用idr机制)
                            -->drm_gem_handle_create_tail(handle)
                                -->int ret = idr_alloc(&dev->mode_config.crtc_idr, ...) //申请一个id并将该id与obj指针关联,该函数返回申请到的id值,(注意此处的参数crtc_idr)
                                /*
                                    handle = ret; //至此,已将应用空间传入的args->handle填充
                                */
                                -->drm_vma_node_allow(&obj->vma_node, file_priv->filp);
                                -->gem_open_object(obj)
            
             
             

              至此,alloc_arg.handle, alloc_arg.pitch和alloc_arg.size都被填充

                1. 创建用于mmap的offset

              前面创建了dumb buffer,在将其映射到应用空间时需要offset(映射点偏移量)参数;

              struct drm_mode_map_dumb mmap_arg;
              mmap_arg.handle = alloc_arg.handle; //将create_dumb返回的handle赋值给mmap_arg结构
              drmIoctl(DRM_IOCTL_MODE_MAP_DUMB)
              内核:
              drm_mode_mmap_dumb_ioctl()
                  -->rockchip_gem_dumb_map_offset(args->handle, &args->offset) //传入mmap_arg结构的handle和offset
                      -->obj = drm_gem_object_lookup(handle) //struct drm_gem_object *obj
                          -->idr_find() //通过id号查找对应的结构
                          /*
                              struct drm_gem_object *obj;
                              obj = idr_find(&filp->object_idr, handle); //获取create_bumb时和该handle绑定的obj指针
                          */
                      -->drm_gem_create_mmap_offset(obj) //为一个object创建一个虚假的mmap偏移量
                      //GEM内存映射通过将伪造的mmap偏移返回给用户空间来工作,该偏移可以在后续的mmap调用中使用,然后,DRM核心代码根据偏移量查找对象并设置各种内存映射结构.
                      //DRM通过通过mmap offset参数传递的伪偏移量来标识要映射的GEM对象.因此,在映射之前,GEM对象必须与假偏移量关联.为,驱动程序必须在对象上调用drm_gem_create_mmap_offset
                      -->*offset = drm_vma_node_offset_addr(&obj->vma_node); //填充应用传入的mmap_arg结构的offset
                      //offset值会根据调用DRM_IOCTL_MODE_MAP_DUMB的次数持续增大,返回后用于mmap的偏移参数,即保证了mmap多次时映射的buffer偏移不会重合
              
               
               

                至此,mmap_arg.offset被填充

                  1. mmap映射bumd buffer到应用空间
                    map = mmap(0, alloc_arg.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mmap_arg.offset);
                
                 
                 
                    1. 申请dma_buf与fd并将二者关联

                  将前面被内核填充的alloc_arg.handle作为参数传给内核,内核使用该值寻找和其绑定的drm_gem_object结构,在其中寻找是否以有dma_buf被export,若有则不再申请,若无则申请.

                  drmPrimeHandleToFD(fd, alloc_arg.handle, 0, &buffer->dmabuf_fd) //dmabuf_fd:待填充的fd
                  /*
                      struct drm_prime_handle args;
                      args.fd = -1;
                  	args.handle = handle;
                  	args.flags = flags;
                  */
                      -->drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &args) //将handle,dmabuf_fd,flags传入内核
                  内核:
                  drm_prime_handle_to_fd_ioctl()
                      -->drm_gem_prime_handle_to_fd(struct drm_device *dev,struct drm_file *file_priv, uint32_t handle,uint32_t flags,int *prime_fd)
                          -->obj = drm_gem_object_lookup(dev, file_priv, handle) //获取与handle绑定的drm_gem_object结构
                          -->dmabuf = drm_prime_lookup_buf_by_handle(&file_priv->prime, handle); //查找是否有与handle绑定的被export的dma_buf
                          /*
                              if(dmabuf) get_dma_buf(dmabuf); //若存在,则获取和该dma_buf绑定的file结构
                          */
                          /*
                              if (obj->import_attach) { //若已有import的dma_buf,重新export该dma_buf
                                  dmabuf = obj->import_attach->dmabuf; //获取该dma_buf
                                  get_dma_buf(dmabuf);
                              }
                          */
                          /*
                              if (obj->dma_buf) { //判断obj结构中是否已申请dma_buf
                                  get_dma_buf(obj->dma_buf);
                                  dmabuf = obj->dma_buf;
                              }
                          */
                              -->dmabuf = export_and_register_object(dev, obj, flags); //前三者都不满足,则申请dma_buf,并注册到该obj中
                                  -->dmabuf = dev->driver->gem_prime_export(dev, obj, flags); //drm_gem_prime_export()
                                  /*
                                      struct dma_buf_export_info exp_info = {
                                          .exp_name = KBUILD_MODNAME,
                                          .owner = dev->driver->fops->owner,
                                          .ops = &drm_gem_prime_dmabuf_ops, //dma_buf_ops操作集
                                          .size = obj->size,
                                          .flags = flags,
                                          .priv = obj,
                                      };
                                  */
                                      -->dma_buf_export(&exp_info) //传入exp_info结构
                                      /*
                                          dmabuf = kzalloc(alloc_size, GFP_KERNEL); //申请dma_buf
                                          dmabuf->priv = exp_info->priv; //填充
                                          dmabuf->ops = exp_info->ops;
                                          dmabuf->size = exp_info->size;
                                          dmabuf->exp_name = exp_info->exp_name;
                                          dmabuf->owner = exp_info->owner;
                                          reservation_object_init(exp_info->resv);
                                          file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf,exp_info->flags); //创建与该dma_buf关联的file结构
                                          dmabuf->file = file;
                                          return dmabuf; //返回生成的dma_buf
                                      */
                                  /*
                                      obj->dma_buf = dmabuf; //加入obj结构
                                  */
                                  -->get_dma_buf(obj->dma_buf);
                                  -->drm_gem_object_reference(obj); //获取新的内核引用
                              -->drm_prime_add_buf_handle(&file_priv->prime,dmabuf, handle); //将申请的dma_buf信息加入drm_prime_file_private->head链表
                              -->*prime_fd = dma_buf_fd(dmabuf, flags) //生成和dma_buf管理的fd,详见下方附录
                  
                   
                   

                  在此获取到的dmafd会传到v4l2插件,如此v4l2插件便可使用该fd找到申请的dma_buf,完成相机数据的输入;

                    1. 为指定的crtc创建framebuffer并获取创建的id(fb仅仅为显存描述信息如宽,高,bpp等,并不是开辟存储图片的内存)
                      DRM_IOCTL(fd, DRM_IOCTL_MODE_ADDFB2, &f)
                      /*
                      struct drm_mode_fb_cmd2 f;
                      f.width  = width;
                  	f.height = height;
                  	f.pixel_format = pixel_format;
                  	f.flags = flags;
                  	memcpy(f.handles, bo_handles, 4 * sizeof(bo_handles[0]));  //DRM_IOCTL_MODE_CREATE_DUMB时填充,用于查找之前创建的drm_gem_object结构
                  	memcpy(f.pitches, pitches, 4 * sizeof(pitches[0]));  //DRM_IOCTL_MODE_CREATE_DUMB时填充
                  	memcpy(f.offsets, offsets, 4 * sizeof(offsets[0]));  //0
                      */
                  
                   
                   

                  内核:

                  drm_mode_addfb2()
                      -->struct drm_framebuffer *fb = internal_framebuffer_create(..., r)
                      /*
                          struct drm_framebuffer *fb;
                          struct drm_mode_fb_cmd2 *r = data;  //用户空间传入的参数
                          fb = internal_framebuffer_create(dev, r, file_priv);
                      */
                          -->rockchip_user_fb_create(..., r) //.fb_create()
                          /*
                              fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); //应用传入的参数
                              static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
                                  .fb_create = rockchip_user_fb_create,
                                  ...
                              };
                          */
                              -->struct drm_gem_object *obj = drm_gem_object_lookup(r->handles[i]) //循环:通过传入的handles找出对应的drm_gem_object结构
                              -->struct drm_framebuffer *fb = rockchip_fb_alloc(r, objs) //objs:循环得到的所有的drm_gem_object结构数组
                                  -->drm_helper_mode_fill_fb_struct(..., &rockchip_fb->fb, r)  //用r的参数填充rockchip_fb->fb结构
                                  /*
                                      struct rockchip_drm_fb *rockchip_fb;
                                  */
                                  -->drm_framebuffer_init(..., &rockchip_fb->fb,&rockchip_drm_fb_funcs)  //创建fb id并加入fb链表
                                  /*
                                      static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
                                          .destroy	= rockchip_drm_fb_destroy,
                                          .create_handle	= rockchip_drm_fb_create_handle,
                                      };
                                  */
                                      -->drm_mode_object_get(..., fb, DRM_MODE_OBJECT_FB) //创建fb id
                      -->r->fb_id = fb->base.id  //将创建的fd id复制给用户空间传入的结构下
                  
                   
                   

                  结构解析

                    1. _drmModeEncoder
                  typedef struct _drmModeEncoder {
                  	uint32_t encoder_id;
                  	uint32_t encoder_type;
                  	uint32_t crtc_id;
                  	uint32_t possible_crtcs; //connector下连接若干encoder,每个encoder支持若干crtc,possible_crtcs的某一位为1代表相应次序(不是id)的crtc可用。
                  	uint32_t possible_clones;
                  }
                  
                   
                   
                    • 2 ._drmModePlane, plane在crtc和framebuffer之间,连接二者
                    typedef struct _drmModePlane {
                    	uint32_t count_formats;
                    	uint32_t *formats;
                    	uint32_t plane_id;
                    	uint32_t crtc_id;  //连接的crtc
                    	uint32_t fb_id;  //连接的framebuffer
                    	uint32_t crtc_x, crtc_y;
                    	uint32_t x, y;
                    	uint32_t possible_crtcs;
                    	uint32_t gamma_size;
                    }
                    
                     
                     

                      其中crtc_id和fb_id都不是指针,由此可以看出一个plane只能对应一个fb_id和一个crtc_id.

                      附录

                      dma-buf机制

                        1. 概念 :

                      dma_buf是内核中一个独立的子系统,提供了一个让不同设备、子系统之间进行共享缓存的统一框架,这里说的缓存通常是指通过DMA方式访问的和硬件交互的内存。 比如,来自摄像头采集的通过pciv驱动传输的内存、gpu内部管理的内存等等.一开始,dma_buf机制在内核中的主要运用场景是支持GPU驱动中的prime机制,但是作为内核中的通用模块,它的适用范围很广.

                        1. dma_buf子系统包含三个主要组成
                      1. dma-buf对象,它代表的后端是一个sg_table,它暴露给应用层的接口是一个文件描述符,通过传递描述符达到了交互访问dma-buf对象,进而最终达成了 共享访问sg_table的目的.
                        2. fence对象, which provides a mechanism to signal when one device as finished access.
                        3. reservation对象, 它负责管理缓存的分享和互斥访问.
                        1. 架构 :

                      DMA_BUF框架下主要有两个角色对象,一个是exporter,相当于是buffer的生产者,相对应的是importer或者是user,即buffer的消费使用者.

                      exporter职责:

                      1. 实现struct amd_buf_ops中的buffer管理回调函数
                        4. 允许使用者通过amd_buf的sharing APIS来共享导出的buffer
                        5. 通过struct dma_buf结构体管理buffer的分配、包装等细节工作
                        6. 决策buffer的实际后端内存的来源
                        7. 管理好scatterlist的迁移工作

                      importer职责:

                      1. 是共享buffer的使用者之一
                        8. 无需关心所用buffer是哪里以及如何产生的
                        9. 通过struct dma_buf_attachment结构体访问用于构建buffer的scatterlist,并且提供将buffer映射到自己地址空间的机制
                        1. 使用流程

                      a) Exporter驱动申请或者引用已经导入的待共享访问的内存
                      b) Exporter驱动调用dma_buf_export()创建dma_buf对象,同时将自定义的struct dma_buf_ops方法集和步骤1中的内存挂载到dma_buf对象中
                      c) Exporter驱动调用dma_buf_fd()将步骤2中创建的dam_buf对象关联到全局可见的文件描述符fd,同时通过ioctl方法将fd传递给应用层
                      d). 应用层将fd传递给importer驱动程序
                      e) importer驱动通过调用dma_buf_get(fd)获取dma_buf对象
                      f) importer驱动调用dma_buf_attach()和dma_buf_map_attachment()获取共享缓存的信息

                        1. 代码示例

                      a) exporter申请dma bufferSS

                      drm_gem_prime_export()
                      /*
                          struct dma_buf_export_info exp_info = {
                      		.exp_name = KBUILD_MODNAME,
                      		.owner = dev->driver->fops->owner,
                      		.ops = &drm_gem_prime_dmabuf_ops,
                      		.size = obj->size,
                      		.flags = flags,
                      		.priv = obj,
                          }; 
                      */
                          -->dma_buf_export(&exp_info)
                          /*
                              struct dma_buf *dmabuf;
                              struct file *file;
                              dmabuf = kzalloc(alloc_size, GFP_KERNEL);
                              dmabuf->priv = exp_info->priv;
                              dmabuf->ops = exp_info->ops;
                              dmabuf->size = exp_info->size;
                              dmabuf->exp_name = exp_info->exp_name;
                              dmabuf->owner = exp_info->owner;
                              file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf,exp_info->flags) //创建file文件对象实例,以及匿名 inode节点和dentry 等数据结构
                              //dmabuf通过anon_inode_getfile()函数挂载到了file对象上的 priv指针上,而dma_buf_fops回调函数集挂载在file对象上的ops上,最后dma_buf_fops函数集中的回调函数实现都会通过file->priv拿到dma_buf对象, 然后直接调用dma_buf中的ops方法。这样的函数重载实现是file作为驱动程序接口功能实现的常规操作
                              dmabuf->file = file; //将生成的file结构与dmabuf关联
                          */
                      
                       
                       

                        b) 将创建的dma_buf与文件描述符fd关联

                        int fd = dma_buf_fd(dmabuf, flags);
                        /*
                            fd = get_unused_fd_flags(flags); //获取一个未使用的数字作为fd返回
                            fd_install(fd, dmabuf->file); //完成fd与file结构的关联,如此,通过fd就能访问file结构的资源
                        */
                        
                         
                         

                        ps:

                          1.file结构:当进程打开一个文件时,内核就会为该进程分配一个file结构,表示打开的文件在进程的上下文,然后应用程序会通过一个int类型的文件描述符来访问这个结构,实际上内核的进程里面维护一个file结构的数组,而文件描述符就是相应的file结构在数组中的下标

                          2.参考:https://blog.csdn.net/abc3240660/article/details/81942190#t6
                          3.转载地址:https://blog.csdn.net/user_jiang/article/details/105178965

                          Logo

                          更多推荐