/* (C)2014, Manuel Bachmann */ /* compile with : gcc ... `pkg-config --cflags --libs libudev libdrm gbm egl` -ldl */ #include #include /* for calloc() */ #include /* for UDEV stuff */ #include /* for O_RDWR, O_CLOEXEC */ #include /* for fstat() */ #include /* for DRM stuff */ #include #include /* for dlopen(), RTLD_LAZY, RTLD_GLOBAL */ #include /* for GBM stuff */ #include /* for EGL stuff */ int main (int argc, char *argv[]) { /* we try to find the first GPU with UDEV */ struct udev *udev; struct udev_enumerate *uenu; struct udev_list_entry *entry; struct udev_device *device, *pci_device, *drm_device = NULL; const char *path, *id; udev = udev_new (); uenu = udev_enumerate_new (udev); udev_enumerate_add_match_subsystem (uenu, "drm"); udev_enumerate_add_match_sysname (uenu, "card[0-9]*"); udev_enumerate_scan_devices (uenu); udev_list_entry_foreach (entry, udev_enumerate_get_list_entry(uenu)) { path = udev_list_entry_get_name (entry); device = udev_device_new_from_syspath (udev, path); if (!device) continue; pci_device = udev_device_get_parent_with_subsystem_devtype (device, "pci", NULL); /* parent bus is PCI/PCIe */ if (pci_device) { /* GPU having the "boot_vga" attribute to "1" is primary */ id = udev_device_get_sysattr_value (pci_device, "boot_vga"); if (id && strcmp(id, "1") == 0) { printf ("Found primary GPU !\n"); drm_device = device; break; } printf ("Found potential GPU...\n"); drm_device = device; } udev_device_unref (device); } udev_enumerate_unref (uenu); if (!drm_device) { printf ("No DRM device found, exiting...\n"); return -1; } /* we try to initialize KMS on our GPU */ const char *f_cardX; int fd; struct stat s; drm_magic_t magic; /* retrieve the real file name (/dev/dri/card0 e.g.) */ f_cardX = udev_device_get_devnode (drm_device); printf ("GPU device path is %s\n", f_cardX); /* we need RW access to the file, better be root */ fd = open (f_cardX, O_RDWR | O_CLOEXEC); if (fd == -1) { printf ("Cannot open %s in read-write mode\n", f_cardX); return -1; } /* let's do a pedantic check ; has the file the DRM_MAJOR flag ? Linux has 226, OpenBSD 81, NetBSD 180 ... */ fstat (fd, &s); if (major(s.st_rdev) != 226) { printf("%s misses the DRM_MAJOR flag\n", f_cardX); return -1; } /* even more pedantic now ; can we use libdrm functions on it ? */ if ((drmGetMagic (fd, &magic) != 0) && (drmAuthMagic (fd, magic) != 0)) { printf ("Cannot use drmGetMagic() and drmAuthMagic() on %s\n", f_cardX); return -1; } /* we try to create an abstract GBM buffer display in our GPU */ struct gbm_device *gbm_display; /* calling gdm_create_device() will automatically dlopen() the DRI driver ; but this will generate errors about missing "_glapi_" symbols. Manually dlopen() "libglapi.so" with RTLD_GLOBAL to make them available. */ dlopen ("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL); gbm_display = gbm_create_device (fd); if (!gbm_display) { printf ("Could not create an abstract GBM buffer display\n"); return -1; } /* we try to initialize EGL on our GBM "display" */ EGLDisplay edpy; EGLContext ectx; EGLConfig ecfg, *ecfgs; EGLint major, minor, count, matched; int iter; edpy = eglGetDisplay (gbm_display); if (edpy == EGL_NO_DISPLAY) { printf ("Could not get EGL display from GBM device\n"); return -1; } if (!eglInitialize (edpy, &major, &minor)) { printf ("Could not initialize EGL display\n"); return -1; } eglGetConfigs (edpy, NULL, 0, &count); ecfgs = calloc (count, sizeof *ecfgs); static const EGLint eattrs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 0, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, EGL_NONE }; eglChooseConfig (edpy, eattrs, ecfgs, count, &matched); for (iter = 0; iter < matched; iter++) { ecfg = ecfgs[iter]; } /* we try to discover DRM connectors and modes */ drmModeRes *resources; drmModeConnector *connector; drmModeModeInfo *info; int i,j; resources = drmModeGetResources (fd); if (!resources) { printf ("Getting DRM resources failed\n"); return -1; } for (i = 0; i < resources->count_connectors; i++) { connector = drmModeGetConnector (fd, resources->connectors[i]); if (connector == NULL) continue; /* if the connector is plugged, list modes avaiable on it */ if (connector->connection == DRM_MODE_CONNECTED) { for (j = 0; j < connector->count_modes; j++) { info = &connector->modes[j]; /* if this is the "preferred" display mode, show it */ if (info->type & DRM_MODE_TYPE_PREFERRED) printf (" PREFERRED :\n"); printf("MODE %d - RESOLUTION : %dx%d\n", j, info->hdisplay, info->vdisplay); } } drmModeFreeConnector (connector); } /* we try to create a GBM surface having the size of the chosen resolution */ struct gbm_surface *gbm_surface; gbm_surface = gbm_surface_create (gbm_display, 800, 600, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); if (!gbm_surface) { printf ("Could not create a GBM surface\n"); return -1; } /* and now create an EGL context/surface on it */ EGLSurface *esurf; esurf = eglCreateWindowSurface (edpy, ecfg, gbm_surface, NULL); if (esurf == EGL_NO_SURFACE) { printf ("Could not create an EGL surface\n"); return -1; } static const EGLint ecattrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; /* THIS SHOULD CRASH HERE ! */ ectx = eglCreateContext (edpy, ecfg, EGL_NO_CONTEXT, ecattrs); gbm_surface_destroy (gbm_surface); drmModeFreeResources (resources); gbm_device_destroy (gbm_display); close (fd); udev_device_unref (drm_device); return 0; }