• KWin、libdrm、DRM从上到下全过程 —— drmModeAddFBxxx(1)


    序言

    最近在研究libdrm、DRM以及KWin,发现要真正理解Linux图形栈从上到下的机制,最好的、最易于理解的方法是将KWin、libdrm和DRM由上到下的调用过程暨代码统一进行研究,这样才能更好地理清其中的关系,把握总体全貌,因此笔者决定开始做这件事。这无疑是一个庞大的工程(目前来看前无古人),笔者不敢奢望能全部完成,但希望能够尽量走得远一些。闲言少叙,开始漫长的旅程……

    一切从framebuffer开始

    这段旅程从哪里开始?笔者选择从drmModeAddFBxxx函数族开始(包括drmModeAddFB、drmModeAddFB2和drmModeAddFB2WithModifiers函数)。一方面,笔者前段时间刚刚对于这部分做过研究(“DRM全解析 —— ADD_FB”和“DRM全解析 —— ADD_FB2”系列文章);另一方面,framebuffer也是DRM的“最左侧”内容,如下图所示:

    上层 —— KWin代码

    上层KWin的相关代码在KWin源码更目录下的src/backends/drm/drm_buffer.cpp中,代码如下:

    1. std::shared_ptr DrmFramebuffer::createFramebuffer(const std::shared_ptr &buffer)
    2. {
    3. const auto size = buffer->size();
    4. const auto handles = buffer->handles();
    5. const auto strides = buffer->strides();
    6. const auto offsets = buffer->offsets();
    7. uint32_t framebufferId = 0;
    8. int ret;
    9. if (buffer->gpu()->addFB2ModifiersSupported() && buffer->modifier() != DRM_FORMAT_MOD_INVALID) {
    10. uint64_t modifier[4];
    11. for (uint32_t i = 0; i < 4; i++) {
    12. modifier[i] = i < buffer->planeCount() ? buffer->modifier() : 0;
    13. }
    14. ret = drmModeAddFB2WithModifiers(buffer->gpu()->fd(), size.width(), size.height(), buffer->format(), handles.data(), strides.data(), offsets.data(), modifier, &framebufferId, DRM_MODE_FB_MODIFIERS);
    15. } else {
    16. ret = drmModeAddFB2(buffer->gpu()->fd(), size.width(), size.height(), buffer->format(), handles.data(), strides.data(), offsets.data(), &framebufferId, 0);
    17. if (ret == EOPNOTSUPP && handles.size() == 1) {
    18. ret = drmModeAddFB(buffer->gpu()->fd(), size.width(), size.height(), 24, 32, strides[0], handles[0], &framebufferId);
    19. }
    20. }
    21. if (ret == 0) {
    22. return std::make_shared(buffer, framebufferId);
    23. } else {
    24. return nullptr;
    25. }
    26. }

    在这里,我们的关注重点不放在KWin代码本身的机制,而是聚焦于与libdrm相关的接口函数,即上边提到的3个函数:drmModeAddFB、drmModeAddFB2和drmModeAddFB2WithModifiers。重点关注一下三个函数的接口区别:

    drmModeAddFB(buffer->gpu()->fd(), size.width(), size.height(), 24, 32, strides[0], handles[0], &framebufferId);

    drmModeAddFB2(buffer->gpu()->fd(), size.width(), size.height(), buffer->format(), handles.data(), strides.data(), offsets.data(), &framebufferId, 0);

    drmModeAddFB2WithModifiers(buffer->gpu()->fd(), size.width(), size.height(), buffer->format(), handles.data(), strides.data(), offsets.data(), modifier, &framebufferId, DRM_MODE_FB_MODIFIERS);

    中间层 —— libdrm代码

    drmModeAddFB、drmModeAddFB2和drmModeAddFB2WithModifiers三个函数都在libdrm源码根目录的xf86drmMode.c文件中,代码分别如下:

    • drmModeAddFB
    1. drm_public drmModeFBPtr drmModeGetFB(int fd, uint32_t buf)
    2. {
    3. struct drm_mode_fb_cmd info;
    4. drmModeFBPtr r;
    5. memclear(info);
    6. info.fb_id = buf;
    7. if (drmIoctl(fd, DRM_IOCTL_MODE_GETFB, &info))
    8. return NULL;
    9. if (!(r = drmMalloc(sizeof(*r))))
    10. return NULL;
    11. r->fb_id = info.fb_id;
    12. r->width = info.width;
    13. r->height = info.height;
    14. r->pitch = info.pitch;
    15. r->bpp = info.bpp;
    16. r->handle = info.handle;
    17. r->depth = info.depth;
    18. return r;
    19. }
    • drmModeAddFB2
    1. drm_public int drmModeAddFB2(int fd, uint32_t width, uint32_t height,
    2. uint32_t pixel_format, const uint32_t bo_handles[4],
    3. const uint32_t pitches[4], const uint32_t offsets[4],
    4. uint32_t *buf_id, uint32_t flags)
    5. {
    6. return drmModeAddFB2WithModifiers(fd, width, height,
    7. pixel_format, bo_handles,
    8. pitches, offsets, NULL,
    9. buf_id, flags);
    10. }
    • drmModeAddFB2WithModifiers
    1. drm_public int drmModeAddFB2WithModifiers(int fd, uint32_t width,
    2. uint32_t height, uint32_t pixel_format, const uint32_t bo_handles[4],
    3. const uint32_t pitches[4], const uint32_t offsets[4],
    4. const uint64_t modifier[4], uint32_t *buf_id, uint32_t flags)
    5. {
    6. struct drm_mode_fb_cmd2 f;
    7. int ret;
    8. memclear(f);
    9. f.width = width;
    10. f.height = height;
    11. f.pixel_format = pixel_format;
    12. f.flags = flags;
    13. memcpy(f.handles, bo_handles, 4 * sizeof(bo_handles[0]));
    14. memcpy(f.pitches, pitches, 4 * sizeof(pitches[0]));
    15. memcpy(f.offsets, offsets, 4 * sizeof(offsets[0]));
    16. if (modifier)
    17. memcpy(f.modifier, modifier, 4 * sizeof(modifier[0]));
    18. if ((ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ADDFB2, &f)))
    19. return ret;
    20. *buf_id = f.fb_id;
    21. return 0;
    22. }

    从代码上就能看出来,drmModeAddFB2()和drmModeAddFB2WithModifiers()走的是一路,走的是DRM_IOCTL_MODE_ADDFB2;而drmModeAddFB是单一路,走的是DRM_IOCTL_MODE_ADDFB(其实这也就是笔者之前有两个系列文章“DRM全解析 —— ADD_FB”和“DRM全解析 —— ADD_FB2”的原因)。

    底层 —— DRM代码

    DRM_IOCTL_MODE_ADDFB和DRM_IOCTL_MODE_ADDFB2分别对应到底层内核DRM的代码。在Linux内核源码根目录下的drivers/gpu/drm/drm_ioctl.c中,代码如下:

    1. DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb_ioctl, 0),
    2. DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2_ioctl, 0),

    drm_mode_addfb_ioctl和drm_mode_addfb2_ioctl函数都在Linux内核源码根目录下的drivers/gpu/drm/drm_framebuffer.c中,代码分别如下:

    • drm_mode_addfb_ioctl
    1. int drm_mode_addfb_ioctl(struct drm_device *dev,
    2. void *data, struct drm_file *file_priv)
    3. {
    4. return drm_mode_addfb(dev, data, file_priv);
    5. }
    1. /**
    2. * drm_mode_addfb - add an FB to the graphics configuration
    3. * @dev: drm device for the ioctl
    4. * @or: pointer to request structure
    5. * @file_priv: drm file
    6. *
    7. * Add a new FB to the specified CRTC, given a user request. This is the
    8. * original addfb ioctl which only supported RGB formats.
    9. *
    10. * Called by the user via ioctl, or by an in-kernel client.
    11. *
    12. * Returns:
    13. * Zero on success, negative errno on failure.
    14. */
    15. int drm_mode_addfb(struct drm_device *dev, struct drm_mode_fb_cmd *or,
    16. struct drm_file *file_priv)
    17. {
    18. struct drm_mode_fb_cmd2 r = {};
    19. int ret;
    20. if (!drm_core_check_feature(dev, DRIVER_MODESET))
    21. return -EOPNOTSUPP;
    22. r.pixel_format = drm_driver_legacy_fb_format(dev, or->bpp, or->depth);
    23. if (r.pixel_format == DRM_FORMAT_INVALID) {
    24. drm_dbg_kms(dev, "bad {bpp:%d, depth:%d}\n", or->bpp, or->depth);
    25. return -EINVAL;
    26. }
    27. /* convert to new format and call new ioctl */
    28. r.fb_id = or->fb_id;
    29. r.width = or->width;
    30. r.height = or->height;
    31. r.pitches[0] = or->pitch;
    32. r.handles[0] = or->handle;
    33. ret = drm_mode_addfb2(dev, &r, file_priv);
    34. if (ret)
    35. return ret;
    36. or->fb_id = r.fb_id;
    37. return 0;
    38. }
    • drm_mode_addfb2_ioctl
    1. int drm_mode_addfb2_ioctl(struct drm_device *dev,
    2. void *data, struct drm_file *file_priv)
    3. {
    4. #ifdef __BIG_ENDIAN
    5. if (!dev->mode_config.quirk_addfb_prefer_host_byte_order) {
    6. /*
    7. * Drivers must set the
    8. * quirk_addfb_prefer_host_byte_order quirk to make
    9. * the drm_mode_addfb() compat code work correctly on
    10. * bigendian machines.
    11. *
    12. * If they don't they interpret pixel_format values
    13. * incorrectly for bug compatibility, which in turn
    14. * implies the ADDFB2 ioctl does not work correctly
    15. * then. So block it to make userspace fallback to
    16. * ADDFB.
    17. */
    18. drm_dbg_kms(dev, "addfb2 broken on bigendian");
    19. return -EOPNOTSUPP;
    20. }
    21. #endif
    22. return drm_mode_addfb2(dev, data, file_priv);
    23. }
    1. /**
    2. * drm_mode_addfb2 - add an FB to the graphics configuration
    3. * @dev: drm device for the ioctl
    4. * @data: data pointer for the ioctl
    5. * @file_priv: drm file for the ioctl call
    6. *
    7. * Add a new FB to the specified CRTC, given a user request with format. This is
    8. * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
    9. * and uses fourcc codes as pixel format specifiers.
    10. *
    11. * Called by the user via ioctl.
    12. *
    13. * Returns:
    14. * Zero on success, negative errno on failure.
    15. */
    16. int drm_mode_addfb2(struct drm_device *dev,
    17. void *data, struct drm_file *file_priv)
    18. {
    19. struct drm_mode_fb_cmd2 *r = data;
    20. struct drm_framebuffer *fb;
    21. if (!drm_core_check_feature(dev, DRIVER_MODESET))
    22. return -EOPNOTSUPP;
    23. fb = drm_internal_framebuffer_create(dev, r, file_priv);
    24. if (IS_ERR(fb))
    25. return PTR_ERR(fb);
    26. drm_dbg_kms(dev, "[FB:%d]\n", fb->base.id);
    27. r->fb_id = fb->base.id;
    28. /* Transfer ownership to the filp for reaping on close */
    29. mutex_lock(&file_priv->fbs_lock);
    30. list_add(&fb->filp_head, &file_priv->fbs);
    31. mutex_unlock(&file_priv->fbs_lock);
    32. return 0;
    33. }

    到这里,可以说是“三分归一统”了。在应用层,drmModeAddFB2()和drmModeAddFB2WithModifiers()并在一起,统一使用DRM_IOCTL_MODE_ADDFB2;而在内核层,drmModeAddFB()也最终合并在了一起,统一调用了drm_mode_addfb2函数。

    欲知后事如何,且看下回分解。

  • 相关阅读:
    Redis通用命令和key的层级结构
    Dubbo心跳检测&超时重试机制
    ttkefu在线客服在日常生活中的便利性体验
    c++ std::move和std::forward总结与使用
    挖矿木马基础知识
    python中的各种打断方式、终止代码
    [CISCN2019 总决赛 Day1 Web4]Laravel1
    Linux系统环境下项目部署
    Leetcode_C++之525. Contiguous Array(连续子数组)
    Arduino红外循迹小车代码笔记和常见问题解决
  • 原文地址:https://blog.csdn.net/phmatthaus/article/details/133747645