接前一篇文章:libdrm全解析二十七 ------ 源码全解析(24)
本文参考以下博文:
特此致谢!
上一篇文章对于DRM_IOCTL_MODE_GETCONNECTOR对应的Userspace API _drmModeGetConnector函数的前一部分进行了解析,本文继续解析源码其余部分。再次贴出该函数源码,在xf86drm.c中,如下:
cpp
/*
* 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分配内存空间。struct drmModeConnectorPtr的定义在xf86drmMode.h中,代码如下:
cpp
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中,如下:
cpp
/**
* 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;
};
可以看到,drmModeConnectorPtrd结构与rm_mode_get_connector结构中存在着一对一的关系,因此是能够赋值的。代码片段如下:
cpp
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函数就讲解完了。