libdrm全解析二十八 —— 源码全解析(25)

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

本文参考以下博文:

DRM 驱动程序开发(VKMS)

特此致谢!

上一篇文章对于DRM_IOCTL_MODE_GETCONNECTOR对应的Userspace API _drmModeGetConnector函数的前一部分进行了解析,本文继续解析源码其余部分。再次贴出该函数源码,在xf86drmMode.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函数中的机制类似,在第一次调用drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn)得到prop、mode、encoder的数量、根据count分配好相应空间后,会第二次调用drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn)得到全部信息。

然后由于进行了ioctl系统调用,也要进行判断,看前一次调用中各个资源(prop、mode、encoder)的数量是否少于后一次调用,如果是,则释放之前分配的,重新走以上流程;如果不是,没问题,可以继续往下进行。

接下来,在各个资源分配好了内存空间之后,调用r = drmMalloc(sizeof(*r))为drmModeConnectorPtr r分配内存空间。drmModeConnectorPtr的定义在xf86drmMode.h中,代码如下:

typedef struct _drmModeConnector {
	uint32_t connector_id;
	uint32_t encoder_id; /**< Encoder currently connected to */
	uint32_t connector_type;
	uint32_t connector_type_id;
	drmModeConnection connection;
	uint32_t mmWidth, mmHeight; /**< HxW in millimeters */
	drmModeSubPixel subpixel;

	int count_modes;
	drmModeModeInfoPtr modes;

	int count_props;
	uint32_t *props; /**< List of property ids */
	uint64_t *prop_values; /**< List of property values */

	int count_encoders;
	uint32_t *encoders; /**< List of encoder ids */
} drmModeConnector, *drmModeConnectorPtr;

接下来通过conn初始化r。

conn的类型是struct drm_mode_get_connector,而r的类型是drmModeConnectorPtr,能够通过conn给r赋值吗?这里再次贴出struct drm_mode_get_connector的源码,在include/drm/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;
};

可以看到,drmModeConnectorPtr结构与rm_mode_get_connector结构中存在着一对一的关系,因此是能够赋值的。代码片段如下:

    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;

此过程中通过drmAllocCpy函数分配空间并拷贝conn中的内存空间。

最后,通过drmFree函数释放掉drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn)分配的struct drm_mode_get_connector conn相关资源,其属于中间临时性质。

到这里,_drmModeGetConnector函数就讲解完了。

猜你喜欢

转载自blog.csdn.net/phmatthaus/article/details/132561778