Análisis completo de libdrm veintisiete: análisis completo del código fuente (24)

Continuación del artículo anterior: Análisis completo de libdrm veintiséis: análisis completo del código fuente (23)

Este artículo hace referencia a las siguientes publicaciones de blog:

Desarrollo de controladores DRM (VKMS)

¡Muchas gracias!

Este artículo comienza a analizar la API de espacio de usuario _drmModeGetConnector() correspondiente a DRM_IOCTL_MODE_GETCONNECTOR. Publique el código fuente de esta función nuevamente, en xf86drm.c, de la siguiente manera:

/*
 * 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;
}

Esta función parece bastante larga, pero de hecho, muchos de los códigos son los mismos que la función drmModeGetResources anterior, que hemos analizado antes. Por ejemplo: memclear(), drmIoctl(), VOID2U64(), U642VOID(), drmMalloc(), drmAllocCpy(), drmFree(), etc. Así que aquí ya no nos centramos en la implementación de estas funciones, sino en la lógica del código.

Primero mire los parámetros de la función:

  • intfd

fd es un descriptor de archivo abierto previamente con una declaración como:

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

Connector_id es el valor obtenido de la información de los conectores obtenida por la función drmModeGerResources en el paso anterior, de la siguiente manera:

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

En realidad, la sonda se pasa cuando la función drmModeGetConnector llama a la función _drmModeGetConnector y el valor es 1. De hecho, cuando el proceso general llama a la función drmModeGetConnector, solo se pasan dos parámetros:

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

Es decir, la función drmModeGetConnector tiene sólo dos parámetros. La función drmModeGetConnector agrega un parámetro adicional al llamar a la función _drmModeGetConnector, que es esta sonda:

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

La razón por la que hay un parámetro más aquí es porque hay una función similar a continuación y es necesario distinguir estas dos funciones:

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

Se puede ver que una de estas dos funciones es obtener el Conector según el número de identificación y la otra es obtener el Conector actual. El fragmento de código correspondiente en la función _drmModeGetConnector es el siguiente:

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

Aquí, para facilitar la comprensión, se proporciona la definición de struct drm_mode_get_connector conn. En include/libdrm/drm_mode.h, el código es el siguiente:

/**
 * 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;
};

El significado del código adicional es similar al de la función drmModeGetResources: a través del primer drmIoctl (fd, DRM_IOCTL_MODE_GETCONNECTOR, & conn), primero se obtiene el número (recuento) de cada elemento y luego se asigna el espacio de memoria del tamaño correspondiente. según el número. El fragmento de código es el siguiente:

    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;
	}
 

Los comentarios en la estructura de borde en el sentido de count_props, prop_ptr, prop_values_ptr, count_modes, modos_ptr, count_encoders y encoders_ptr están escritos en detalle, por lo que no entraré en detalles aquí.

Hablemos aquí de la estructura drm_mode_modeinfo. La estructura está definida en include/libdrm/drm_mode.h, el código es el siguiente:

/**
 * 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];
};

Los miembros de esta estructura me recuerdan a aquellas estructuras en el FrameBuffer del núcleo, y el último comentario en la estructura también confirma este punto. Los miembros de la estructura son parámetros relacionados con la visualización, como sincronización de línea, sincronización de campo, etc.

El miembro de struct drm_mode_get_connector relacionado con struct drm_mode_modeinfo es __u64 modos_ptr. Puedes ver a través del código de 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;

Varios otros miembros apuntan a tipos iguales o similares, solo que modos_ptr apunta a la matriz de estructura drm_modeinfo.

En el próximo artículo, continuaremos analizando el código fuente de seguimiento de la función _drmModeGetConnector.

Guess you like

Origin blog.csdn.net/phmatthaus/article/details/132542833