赞
踩
DisplayLink是USB接口的显示器(USB显示器)的实现技术,支持windows、Linux、macOS等。windows10的DisplayLink驱动可以支持GPU渲染加速(超出了本人的知识范围,不做详细描述),但Linux上的DisplayLink驱动只能使用CPU渲染,本文主要分析Linux上的DisplayLink驱动,下文描述的DisplayLink都是指Linux上的DisplayLink驱动,下文不再赘述。
本文使用的DisplayLink的源码版本为 evdi-1.14.4 版本,下载地址为GitHub - DisplayLink/evdi: Extensible Virtual Display Interface
Linux上的DisplayLink驱动(称为evdi)包含内核态DRM驱动和用户态libevdi库等,其整体框架如下图所示:
evdi驱动中使用了platform设备(平台设备)和drm设备。平台设备用于管理(添加和删除)drm设备。drm设备用于向显示服务器提供渲染处理服务,evdi支持多个drm设备。
注意:evdi驱动的代码中有建立usb与evdi的平台设备的关系的逻辑,这部分逻辑只是为了能在sysfs中体现evdi平台设备与usb显示器之间的关系而言,暂无额外功能。
- evdi_init (evdi_platform_drv.c) evdi驱动模块初始化
- 调用 root_device_register 为evdi创建根设备
- 设置 g_ctx.usb_notifier.notifier_call 为 evdi_platform_drv_usb
- 调用 usb_register_notify 注册usb通知块 g_ctx.usb_notifier
- evdi_sysfs_init (evdi_sysfs.c) 为evdi的根设备创建sysfs文件
- 调用 platform_driver_register 注册evdi的平台设备驱动 evdi_platform_driver
- evdi_platform_add_devices (evdi_platform_drv.c) 添加指定数量的evdi的平台设备
- evdi_exit (evdi_platform_drv.c) evdi驱动模块反初始化
- evdi_platform_remove_all_devices
- 调用 platform_driver_unregister反注册evdi的平台设备驱动 evdi_platform_driver
- evdi_sysfs_exit (evdi_sysfs.c) 删除evdi根设备的sysfs文件
- usb_unregister_notify 反注册usb通知块
- 调用 root_device_unregister 反注册evdi的根设备
evdi的平台设备驱动为evdi_platform_driver(struct platform_driver 类型的静态变量),见:
- evdi_platform_driver (evdi_drv.c)
- .probe = evdi_platform_probe,
- .remove = evdi_platform_remove,
在添加evdi的平台设备后,设备驱动框架会触发evdi的设备发现回调 evdi_platform_probe (
evdi_platform_drv.c) ,在该回调中会添加
evdi的drm设备。而添加evdi的平台设备的方式有两种:
1. 在模块初始化时,注册evdi的平台设备驱动后,在最后会创建指定数量(通过模块的初始化参数指定)的evdi的平台设备;
2. 用户空间程序通过evdi的根设备的sysfs文件添加evdi的平台设备,add_store (evdi_sysfs.c) -> evdi_platform_add_devices (evdi_platform_drv.c);
- evdi_platform_device_probe (evdi_platform_drv.c) 创建evdi的平台设备
- evdi_drm_device_create (evdi_drm_drv.c)
-
- evdi_drm_device_create (evdi_drm_drv.c) 创建evdi的drm设备
- 调用 drm_dev_alloc 创建evdi的drm设备,其驱动为driver (evdi_drm_drv.c)
- evdi_drm_device_init (evdi_drm_drv.c) 初始化evdi的drm设备
- drm_dev_register 注册evdi的drm设备
evdi的drm设备的驱动为driver (evdi_drm_drv.c) (struct drm_driver类型的静态变量):
- driver (evdi_drm_drv.c)
- .open = evdi_driver_open,
- .postclose = evdi_driver_postclose,
- /* gem hooks */
- #if KERNEL_VERSION(5, 11, 0) <= LINUX_VERSION_CODE || defined(EL8)
- #elif KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE
- .gem_free_object_unlocked = evdi_gem_free_object,
- #else
- .gem_free_object = evdi_gem_free_object,
- #endif
- #if KERNEL_VERSION(5, 11, 0) <= LINUX_VERSION_CODE || defined(EL8)
- #else
- .gem_vm_ops = &evdi_gem_vm_ops,
- #endif
- .dumb_create = evdi_dumb_create,
- .dumb_map_offset = evdi_gem_mmap,
- #if KERNEL_VERSION(5, 12, 0) <= LINUX_VERSION_CODE || defined(EL8)
- #else
- .dumb_destroy = drm_gem_dumb_destroy,
- #endif
- .ioctls = evdi_painter_ioctls,
- .num_ioctls = ARRAY_SIZE(evdi_painter_ioctls),
- .fops = &evdi_driver_fops,
- ......
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
evdi的drm设备的自定义ioctl指令列表为:
- evdi_painter_ioctls (evdi_drm_drv.c) (struct drm_ioctl_desc类型的静态变量)
- EVDI_CONNECT --> evdi_painter_connect_ioctl,
- EVDI_REQUEST_UPDATE --> evdi_painter_request_update_ioctl
- EVDI_GRABPIX --> evdi_painter_grabpix_ioctl,
- EVDI_DDCCI_RESPONSE --> evdi_painter_ddcci_response_ioctl
- EVDI_ENABLE_CURSOR_EVENTS --> evdi_painter_enable_cursor_events_ioctl
usb显示器连接和断开的ioctl指令:
- evdi_painter_connect_ioctl (evdi_painter.c) 显示器的连接与断开操作
- 判断是否为显示器连接:
- 若是,则 evdi_painter_connect (evdi_painter.c)
- 否则,evdi_painter_disconnect (evdi_painter.c)
从evdi的drm驱动中抓图的ioctl指令EVDI_GRABPIX(将evdi的帧缓冲区的主图像和光标图像拷贝到用户空间指定的缓冲区中):
- evdi_painter_grabpix_ioctl (evdi_painter.c)
- ...
- merge_dirty_rects
- ...
- copy_primary_pixels
- copy_cursor_pixels
- ...
请求evdi的drm驱动更新图像的ioctl指令,evdi的drm驱动在更新完成后会主动通知用户空间程序更新已完成(通过向drm设备写入evdi的更新已完成的事件),用户空间的libevdi库会监听drm设备的句柄(使用select),当发现drm设备可读时,则会读取并处理evdi内核态的drm驱动写入的事件(参见libevdi的 evdi_handle_events 函数),若是图像更新已完成的事件,则会从evdi的drm驱动中抓取图像,这是一种异步图像更新机制。
- evdi_painter_request_update_ioctl (evdi_painter.c) 请求更新图像
- 设置 struct evdi_painter 类型的 was_update_requested
-
- 若需要则触发已更新完成的事件
- evdi_painter_send_update_ready_if_needed (evdi_painter.c)
- 若 struct evdi_painter 类型的 was_update_requested 为真,则:
- 1. evdi_painter_send_update_ready (evdi_painter.c)
- 2. 设置was_update_requested 为假
-
- evdi_painter_send_update_ready (evdi_painter.c) 触发已更新完成的事件
- 构造 DRM_EVDI_EVENT_UPDATE_READY 更新事件
- evdi_painter_send_event (evdi_painter.c)
先写到这里,具体细节还需自行分析源码。
特别说明:本文仅为本文的浅见,如有错误,烦请指正。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。