aanrudolph2
Posts: 2
Joined: Sat Oct 12, 2019 2:02 am

DRI issues - Bad File Descriptor

Sat Oct 12, 2019 2:13 am

Hi all,

Not sure if this is the right place for this post, but I'll try. I've been looking into accelerated graphics on a Pi Zero, and, more generally, graphics in Linux without X. My goal is to get a better understanding of what's behind the libraries.

I've been following a tutorial of sorts (http://betteros.org/tut/graphics1.php) that goes through framebuffers and DRI, without using the fancy libraries. So far I've been able to successfully run the framebuffer-only (non accelerated) sample code. It's slow, but it works. I tried the DRI code (just below it in the referenced page) on a QEMU x86 Debian system and it worked without a hitch. However, when I ran it on my Pi, some strange things happened. For brevity, the author did not put in a lot of the standard error-handling code, so I ultimately did that myself.

I found that, for some odd reason, the call to ioctl(dri_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb) is throwing an error, saying the file descriptor is invalid. Yet, prior to that ioctl, all of them have passed. Is there a known issue with the Pi DRI driver that is causing this, or should I be approaching this differently?

My code is attached. Please see the site for reference. Thanks in advance.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <libdrm/drm.h>
#include <libdrm/drm_mode.h>

#include <errno.h>
#include <string.h>

int main()
{
	//------------------------------------------------------------------------------
	//Opening the DRI device
	//------------------------------------------------------------------------------

	int dri_fd = open("/dev/dri/card0",O_RDWR | O_CLOEXEC);

	if(dri_fd == -1)
	{
		fprintf(stderr, "Can't open DRI interface: %s\n", strerror(errno));
		exit(1);
	}
	
	//------------------------------------------------------------------------------
	//Kernel Mode Setting (KMS)
	//------------------------------------------------------------------------------

	uint64_t res_fb_buf[10]={0},
			res_crtc_buf[10]={0},
			res_conn_buf[10]={0},
			res_enc_buf[10]={0};

	struct drm_mode_card_res res={0};

	//Become the "master" of the DRI device
	if(ioctl(dri_fd, DRM_IOCTL_SET_MASTER, 0) == -1)
	{
		fprintf(stderr, "Can't get master: %s\n", strerror(errno));
		exit(1);
	}

	//Get resource counts
	if(ioctl(dri_fd, DRM_IOCTL_MODE_GETRESOURCES, &res) == -1)
	{
		fprintf(stderr, "Can't get resource counts: %s\n", strerror(errno));
		exit(1);
	}
	res.fb_id_ptr=(uint64_t)res_fb_buf;
	res.crtc_id_ptr=(uint64_t)res_crtc_buf;
	res.connector_id_ptr=(uint64_t)res_conn_buf;
	res.encoder_id_ptr=(uint64_t)res_enc_buf;
	//Get resource IDs
	if(ioctl(dri_fd, DRM_IOCTL_MODE_GETRESOURCES, &res) == -1)
	{
		fprintf(stderr, "Can't get resources: %s\n", strerror(errno));
		exit(1);
	}

	printf("fb: %d, crtc: %d, conn: %d, enc: %d\n",res.count_fbs,res.count_crtcs,res.count_connectors,res.count_encoders);
	
	// printf("res.fb_id_ptr=%u; res.crtc_id_ptr=%u; res.connector_id_ptr=%u; res.encoder_id_ptr=%u\n", res_fb_buf, res_crtc_buf, res_conn_buf, res_enc_buf);

	void *fb_base[10];
	long fb_w[10];
	long fb_h[10];

	//Loop though all available connectors
	int i;
	for (i = 0;i < res.count_connectors; i++)
	{
		struct drm_mode_modeinfo conn_mode_buf[20]={0};
		uint64_t	conn_prop_buf[20]={0},
					conn_propval_buf[20]={0},
					conn_enc_buf[20]={0};

		struct drm_mode_get_connector conn={0};

		conn.connector_id=res_conn_buf[i];

		if(ioctl(dri_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn) == -1)	//get connector resource counts
		{
			fprintf(stderr, "Can't get connector counts: %s\n", strerror(errno));
			exit(1);
		}
		conn.modes_ptr=(uint64_t)conn_mode_buf;
		conn.props_ptr=(uint64_t)conn_prop_buf;
		conn.prop_values_ptr=(uint64_t)conn_propval_buf;
		conn.encoders_ptr=(uint64_t)conn_enc_buf;
		if(ioctl(dri_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn) == -1)	//get connector resources
		{
			fprintf(stderr, "Can't get connectors: %s\n", strerror(errno));
			exit(1);
		}
		// printf("conn.modes_ptr=%u; conn.props_ptr=%u; conn.prop_values_ptr=%u; conn.encoders_ptr=%u\n", conn_mode_buf, conn_prop_buf, conn_propval_buf, conn_enc_buf);

		//Check if the connector is OK to use (connected to something)
		if (conn.count_encoders < 1 || conn.count_modes < 1 || !conn.encoder_id || !conn.connection)
		{
			printf("Not connected\n");
			continue;
		}
		else
		{
			printf("Connection %d : mode: %d, prop: %d, enc: %d\n",conn.connection,conn.count_modes,conn.count_props,conn.count_encoders);
		}
		//------------------------------------------------------------------------------
		//Creating a dumb buffer
		//------------------------------------------------------------------------------
		struct drm_mode_create_dumb create_dumb={0};
		struct drm_mode_map_dumb map_dumb={0};
		struct drm_mode_fb_cmd cmd_dumb={0};

		//If we create the buffer later, we can get the size of the screen first.
		//This must be a valid mode, so it's probably best to do this after we find
		//a valid crtc with modes.
		create_dumb.width = conn_mode_buf[0].hdisplay;
		create_dumb.height = conn_mode_buf[0].vdisplay;
		create_dumb.bpp = 32;
		create_dumb.flags = 0;
		create_dumb.pitch = 0;
		create_dumb.size = 0;
		create_dumb.handle = 0;
		if(ioctl(dri_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb) == -1)
		{
			fprintf(stderr, "Can't create dumb buffer: %s\n", strerror(errno));
		//	exit(1);
		}

		cmd_dumb.width=create_dumb.width;
		cmd_dumb.height=create_dumb.height;
		cmd_dumb.bpp=create_dumb.bpp;
		cmd_dumb.pitch=create_dumb.pitch;
		cmd_dumb.depth=24;
		cmd_dumb.handle=create_dumb.handle;
		ioctl(dri_fd,DRM_IOCTL_MODE_ADDFB,&cmd_dumb);

		map_dumb.handle=create_dumb.handle;
		ioctl(dri_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb);

		fb_base[i] = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED, dri_fd, map_dumb.offset);
		fb_w[i]=create_dumb.width;
		fb_h[i]=create_dumb.height;

		//------------------------------------------------------------------------------
		//Kernel Mode Setting (KMS)
		//------------------------------------------------------------------------------

		printf("modes: %dx%d FB: %d\n",conn_mode_buf[0].hdisplay,conn_mode_buf[0].vdisplay,fb_base[i]);

		struct drm_mode_get_encoder enc={0};

		enc.encoder_id=conn.encoder_id;
		if(ioctl(dri_fd, DRM_IOCTL_MODE_GETENCODER, &enc))	//get encoder
		{
			fprintf(stderr, "Can't get encoder: %s\n", strerror(errno));
		//	exit(1);
		}

		struct drm_mode_crtc crtc={0};

		crtc.crtc_id=enc.crtc_id;
		if(ioctl(dri_fd, DRM_IOCTL_MODE_GETCRTC, &crtc))
		{
			fprintf(stderr, "Can't get CRT controller: %s\n", strerror(errno));
		//	exit(1);
		}

		crtc.fb_id=cmd_dumb.fb_id;
		crtc.set_connectors_ptr=(uint64_t)&res_conn_buf[i];
		crtc.count_connectors=1;
		crtc.mode=conn_mode_buf[0];
		crtc.mode_valid=1;
		if(ioctl(dri_fd, DRM_IOCTL_MODE_SETCRTC, &crtc))
		{
			fprintf(stderr, "Can't set CRT Controller: %s\n", strerror(errno));
		//	exit(1);
		}
	}

	//Stop being the "master" of the DRI device
	ioctl(dri_fd, DRM_IOCTL_DROP_MASTER, 0);

	int x,y;
	for (i=0;i<100;i++)
	{
		int j;
		for (j=0;j<res.count_connectors;j++)
		{
			int col=(rand()%0x00ffffff)&0x00ff00ff;
			for (y=0;y<fb_h[j];y++)
				for (x=0;x<fb_w[j];x++)
				{
					int location=y*(fb_w[j]) + x;
					*(((uint32_t*)fb_base[j])+location)=col;
				}
		}
		usleep(100000);
	}

	return 0;
}

aanrudolph2
Posts: 2
Joined: Sat Oct 12, 2019 2:02 am

Re: DRI issues - Bad File Descriptor

Sat Oct 12, 2019 7:15 pm

An update: Previously I was outputting to a 2K monitor. The mode output from the program said it was running 1920x1080. I just grabbed an older display (one of those monitors that's really a TV) from Goodwill and I'm now getting a new error: No such file or directory. The program states the resolution now is 1360x768. I also saw a visible flicker as the display attempted to go into a graphics mode (could just be that it's older hardware). Could the RAM split have anything to do with this? Or is it just because the VideoCore can't handle higher resolutions well or something (I know that shouldn't make sense)? I know I'm being vague but something isn't adding up.

[EDIT]
Finally got it working. I had to break out of the for() loop at the end where it finishes setting up the CRT Controller. I'm assuming this may be hardware-specific since the Pi Zero has an HDMI and Analog Video out; since I already set an output on the HDMI connector, the Analog one would likely be invalidated. If anyone has any feedback, I'd like to know, especially with respect to the lower versus higher resolutions.

Return to “Graphics programming”