libdrm全解析二十七 —— 源码全解析(24)

接前一篇文章:libdrm全解析二十六 —— 源码全解析(23)

本文参考以下博文:

DRM 驱动程序开发(VKMS)

特此致谢!

本文开始对于DRM_IOCTL_MODE_GETCONNECTOR对应的Userspace API _drmModeGetConnector()进行解析。再次贴出该函数源码,在xf86drm.c中,如下:

/** Connector manipulation*/
static drmModeConnectorPtr
_drmModeGetConnector(int fd, uint32_t connector_id, int probe)
{struct drm_mode_get_connector conn, counts;drmModeConnectorPtr r = NULL;struct drm_mode_modeinfo stack_mode;memclear(conn);conn.connector_id = connector_id;if (!probe) {conn.count_modes = 1;conn.modes_ptr = VOID2U64(&stack_mode);}if (drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))return 0;retry:counts = conn;if (conn.count_props) {conn.props_ptr = VOID2U64(drmMalloc(conn.count_props*sizeof(uint32_t)));if (!conn.props_ptr)goto err_allocs;conn.prop_values_ptr = VOID2U64(drmMalloc(conn.count_props*sizeof(uint64_t)));if (!conn.prop_values_ptr)goto err_allocs;}if (conn.count_modes) {conn.modes_ptr = VOID2U64(drmMalloc(conn.count_modes*sizeof(struct drm_mode_modeinfo)));if (!conn.modes_ptr)goto err_allocs;} else {conn.count_modes = 1;conn.modes_ptr = VOID2U64(&stack_mode);}if (conn.count_encoders) {conn.encoders_ptr = VOID2U64(drmMalloc(conn.count_encoders*sizeof(uint32_t)));if (!conn.encoders_ptr)goto err_allocs;}if (drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))goto err_allocs;/* The number of available connectors and etc may have changed with a* hotplug event in between the ioctls, in which case the field is* silently ignored by the kernel.*/if (counts.count_props < conn.count_props ||counts.count_modes < conn.count_modes ||counts.count_encoders < conn.count_encoders) {drmFree(U642VOID(conn.props_ptr));drmFree(U642VOID(conn.prop_values_ptr));if (U642VOID(conn.modes_ptr) != &stack_mode)drmFree(U642VOID(conn.modes_ptr));drmFree(U642VOID(conn.encoders_ptr));goto retry;}if(!(r = drmMalloc(sizeof(*r)))) {goto err_allocs;}r->connector_id = conn.connector_id;r->encoder_id = conn.encoder_id;r->connection   = conn.connection;r->mmWidth      = conn.mm_width;r->mmHeight     = conn.mm_height;/* convert subpixel from kernel to userspace */r->subpixel     = conn.subpixel + 1;r->count_modes  = conn.count_modes;r->count_props  = conn.count_props;r->props        = drmAllocCpy(U642VOID(conn.props_ptr), conn.count_props, sizeof(uint32_t));r->prop_values  = drmAllocCpy(U642VOID(conn.prop_values_ptr), conn.count_props, sizeof(uint64_t));r->modes        = drmAllocCpy(U642VOID(conn.modes_ptr), conn.count_modes, sizeof(struct drm_mode_modeinfo));r->count_encoders = conn.count_encoders;r->encoders     = drmAllocCpy(U642VOID(conn.encoders_ptr), conn.count_encoders, sizeof(uint32_t));r->connector_type  = conn.connector_type;r->connector_type_id = conn.connector_type_id;if ((r->count_props && !r->props) ||(r->count_props && !r->prop_values) ||(r->count_modes && !r->modes) ||(r->count_encoders && !r->encoders)) {drmFree(r->props);drmFree(r->prop_values);drmFree(r->modes);drmFree(r->encoders);drmFree(r);r = 0;}err_allocs:drmFree(U642VOID(conn.prop_values_ptr));drmFree(U642VOID(conn.props_ptr));if (U642VOID(conn.modes_ptr) != &stack_mode)drmFree(U642VOID(conn.modes_ptr));drmFree(U642VOID(conn.encoders_ptr));return r;
}

这个函数看似挺长,但其实其中很多代码和之前drmModeGetResources函数都是相同的,我们之前已经分析过了。比如:memclear()、drmIoctl()、VOID2U64()、U642VOID()、drmMalloc()、drmAllocCpy()、drmFree()等。所以这里我们不再重点关注这些函数的实现,而是把关注点落在代码的逻辑上。

先来看函数参数:

  • int fd

fd是之前通过诸如以下语句打开的文件描述符:

fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC)
  • uint32_t connector_id

connector_id是通过上一步drmModeGerResources函数得到的connectors的信息获取到的值,如下所示:

    resources = drmModeGetResources(fd);	//获取drmModeRes资源,包含fb、crtc、encoder、connector等conn_id = resources->connectors[0];		//获取connector id
  • int probe

probe实际上是drmModeGetConnector函数调用_drmModeGetConnector函数的时候传入的,值为1。其实一般流程调用drmModeGetConnector函数的时候只传入了两个参数:

connector = drmModeGetConnector(fd, conn_id);	//根据connector_id获取connector资源

也即,drmModeGetConnector函数只有两个参数。而drmModeGetConnector函数在调用_drmModeGetConnector函数的时候多加了一个参数,就是这个probe:

drm_public drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connector_id)
{return _drmModeGetConnector(fd, connector_id, 1);
}

这里之所以多了一个参数,是因为下边还有一个类似函数,要将这两个函数加以区分:

drm_public drmModeConnectorPtr drmModeGetConnectorCurrent(int fd, uint32_t connector_id)
{return _drmModeGetConnector(fd, connector_id, 0);
}

可以看到,这两个函数一个是根据id号得到Connector,一个是得到当前的Connector。在_drmModeGetConnector函数中的相应代码片段如下:

    conn.connector_id = connector_id;if (!probe) {conn.count_modes = 1;conn.modes_ptr = VOID2U64(&stack_mode);}

这里为了便于理解,给出struct drm_mode_get_connector conn的定义,在include/libdrm/drm_mode.h中,代码如下:

/*** struct drm_mode_get_connector - Get connector metadata.** User-space can perform a GETCONNECTOR ioctl to retrieve information about a* connector. User-space is expected to retrieve encoders, modes and properties* by performing this ioctl at least twice: the first time to retrieve the* number of elements, the second time to retrieve the elements themselves.** To retrieve the number of elements, set @count_props and @count_encoders to* zero, set @count_modes to 1, and set @modes_ptr to a temporary struct* drm_mode_modeinfo element.** To retrieve the elements, allocate arrays for @encoders_ptr, @modes_ptr,* @props_ptr and @prop_values_ptr, then set @count_modes, @count_props and* @count_encoders to their capacity.** Performing the ioctl only twice may be racy: the number of elements may have* changed with a hotplug event in-between the two ioctls. User-space is* expected to retry the last ioctl until the number of elements stabilizes.* The kernel won't fill any array which doesn't have the expected length.** **Force-probing a connector**** If the @count_modes field is set to zero and the DRM client is the current* DRM master, the kernel will perform a forced probe on the connector to* refresh the connector status, modes and EDID. A forced-probe can be slow,* might cause flickering and the ioctl will block.** User-space needs to force-probe connectors to ensure their metadata is* up-to-date at startup and after receiving a hot-plug event. User-space* may perform a forced-probe when the user explicitly requests it. User-space* shouldn't perform a forced-probe in other situations.*/
struct drm_mode_get_connector {/** @encoders_ptr: Pointer to ``__u32`` array of object IDs. */__u64 encoders_ptr;/** @modes_ptr: Pointer to struct drm_mode_modeinfo array. */__u64 modes_ptr;/** @props_ptr: Pointer to ``__u32`` array of property IDs. */__u64 props_ptr;/** @prop_values_ptr: Pointer to ``__u64`` array of property values. */__u64 prop_values_ptr;/** @count_modes: Number of modes. */__u32 count_modes;/** @count_props: Number of properties. */__u32 count_props;/** @count_encoders: Number of encoders. */__u32 count_encoders;/** @encoder_id: Object ID of the current encoder. */__u32 encoder_id;/** @connector_id: Object ID of the connector. */__u32 connector_id;/*** @connector_type: Type of the connector.** See DRM_MODE_CONNECTOR_* defines.*/__u32 connector_type;/*** @connector_type_id: Type-specific connector number.** This is not an object ID. This is a per-type connector number. Each* (type, type_id) combination is unique across all connectors of a DRM* device.*/__u32 connector_type_id;/*** @connection: Status of the connector.** See enum drm_connector_status.*/__u32 connection;/** @mm_width: Width of the connected sink in millimeters. */__u32 mm_width;/** @mm_height: Height of the connected sink in millimeters. */__u32 mm_height;/*** @subpixel: Subpixel order of the connected sink.** See enum subpixel_order.*/__u32 subpixel;/** @pad: Padding, must be zero. */__u32 pad;
};

再往下代码的意思和drmModeGetResources函数也是类似的,通过第一次drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn)先得到各个项的数量(count),然后根据数量分配相应大小的内存空间。代码片段如下:

    if (conn.count_props) {conn.props_ptr = VOID2U64(drmMalloc(conn.count_props*sizeof(uint32_t)));if (!conn.props_ptr)goto err_allocs;conn.prop_values_ptr = VOID2U64(drmMalloc(conn.count_props*sizeof(uint64_t)));if (!conn.prop_values_ptr)goto err_allocs;}if (conn.count_modes) {conn.modes_ptr = VOID2U64(drmMalloc(conn.count_modes*sizeof(struct drm_mode_modeinfo)));if (!conn.modes_ptr)goto err_allocs;} else {conn.count_modes = 1;conn.modes_ptr = VOID2U64(&stack_mode);}if (conn.count_encoders) {conn.encoders_ptr = VOID2U64(drmMalloc(conn.count_encoders*sizeof(uint32_t)));if (!conn.encoders_ptr)goto err_allocs;}

count_props、prop_ptr、prop_values_ptr、count_modes、modes_ptr、count_encoders、encoders_ptr的意义上边结构体中的注释写得很详细了,在此不赘述。

这里说一下drm_mode_modeinfo结构。该结构的定义在include/libdrm/drm_mode.h中,代码如下:

/*** struct drm_mode_modeinfo - Display mode information.* @clock: pixel clock in kHz* @hdisplay: horizontal display size* @hsync_start: horizontal sync start* @hsync_end: horizontal sync end* @htotal: horizontal total size* @hskew: horizontal skew* @vdisplay: vertical display size* @vsync_start: vertical sync start* @vsync_end: vertical sync end* @vtotal: vertical total size* @vscan: vertical scan* @vrefresh: approximate vertical refresh rate in Hz* @flags: bitmask of misc. flags, see DRM_MODE_FLAG_* defines* @type: bitmask of type flags, see DRM_MODE_TYPE_* defines* @name: string describing the mode resolution** This is the user-space API display mode information structure. For the* kernel version see struct drm_display_mode.*/
struct drm_mode_modeinfo {__u32 clock;__u16 hdisplay;__u16 hsync_start;__u16 hsync_end;__u16 htotal;__u16 hskew;__u16 vdisplay;__u16 vsync_start;__u16 vsync_end;__u16 vtotal;__u16 vscan;__u32 vrefresh;__u32 flags;__u32 type;char name[DRM_DISPLAY_MODE_LEN];
};

这个结构体中的成员让我不由想起了内核FrameBuffer中的那几个结构体,结构体中最后的注释也印证了这一点。结构体的成员都是显示相关的参数,如行同步、场同步等。

与struct drm_mode_modeinfo相关的struct drm_mode_get_connector中的成员为__u64 modes_ptr。通过struct drm_mode_get_connector的代码可以看到:

    /** @encoders_ptr: Pointer to ``__u32`` array of object IDs. */__u64 encoders_ptr;/** @modes_ptr: Pointer to struct drm_mode_modeinfo array. */__u64 modes_ptr;/** @props_ptr: Pointer to ``__u32`` array of property IDs. */__u64 props_ptr;/** @prop_values_ptr: Pointer to ``__u64`` array of property values. */__u64 prop_values_ptr;

其它几个成员都指向相同或相似类型,只有这个modes_ptr,指向了struct drm_modeinfo的array。

在下一篇文章中将继续对_drmModeGetConnector函数的后续源码进行解析。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部