Changes.
[cairo/gpu.git] / src / gpu / cairo-gpu-impl-space-gallium-drm.h
blobf6079ad62f6dd441108ac4becf231fc92dc5d085
1 #include <dlfcn.h>
3 #include <unistd.h>
4 #include <fcntl.h>
5 #include <sys/ioctl.h>
6 #include <drm/drm.h>
7 #include <errno.h>
8 #include <stdio.h>
10 #define extern extern __attribute__((weak))
11 #include <X11/Xlib.h>
12 #include <X11/Xlibint.h>
13 #include <X11/extensions/dri2tokens.h>
14 #include <X11/extensions/dri2proto.h>
15 #include <X11/extensions/extutil.h>
16 #undef extern
18 static void *driOpenDriver(const char *driverName)
20 void *glhandle, *handle;
21 const char *libPaths, *p, *next;
22 char realDriverName[200];
23 int len;
25 /* Attempt to make sure libGL symbols will be visible to the driver */
26 glhandle = dlopen("libGL.so.1", RTLD_NOW | RTLD_GLOBAL);
28 libPaths = NULL;
29 if (geteuid() == getuid()) {
30 /* don't allow setuid apps to use LIBGL_DRIVERS_PATH */
31 libPaths = getenv("LIBGL_DRIVERS_PATH");
32 if (!libPaths)
33 libPaths = getenv("LIBGL_DRIVERS_DIR"); /* deprecated */
36 if (libPaths == NULL)
37 libPaths = "/usr/lib/dri";
39 handle = NULL;
40 for (p = libPaths; *p; p = next) {
41 next = strchr(p, ':');
42 if (next == NULL) {
43 len = strlen(p);
44 next = p + len;
45 } else {
46 len = next - p;
47 next++;
50 snprintf(realDriverName, sizeof realDriverName,
51 "%.*s/tls/%s_dri.so", len, p, driverName);
53 handle = dlopen(realDriverName, RTLD_NOW | RTLD_GLOBAL);
55 if ( handle == NULL ) {
56 snprintf(realDriverName, sizeof realDriverName,
57 "%.*s/%s_dri.so", len, p, driverName);
58 handle = dlopen(realDriverName, RTLD_NOW | RTLD_GLOBAL);
61 if ( handle != NULL )
62 break;
65 if (glhandle)
66 dlclose(glhandle);
68 return handle;
71 static void*
72 drm_open_driver(const char* driver_name, struct drm_api** papi)
74 void* driver;
75 struct drm_api * (*p_drm_api_create)(void);
76 struct drm_api* api;
78 driver = driOpenDriver(driver_name);
79 if(!driver)
80 return 0;
82 // TODO: this is not necessarily public. We may want to use _egl Main instead; note that we need to check that the name is "DRM/Gallium/Win" since it may be non-Gallium
83 p_drm_api_create = dlsym(driver, "drm_api_create");
84 if(!p_drm_api_create) // non-Gallium driver
85 goto fail;
87 api = p_drm_api_create();
88 if(!api)
89 goto fail;
91 *papi = api;
92 return driver;
94 fail:
95 dlclose(driver);
96 return 0;
99 extern __attribute__((weak)) ssize_t getline(char **lineptr, size_t *n, FILE *stream);
101 static char*
102 drm_get_driver_name(int card)
104 char *driver;
105 size_t driver_len;
106 char path[PATH_MAX];
107 FILE *f;
108 int len;
110 if(!getline)
111 return 0;
113 snprintf(path, sizeof(path), "/sys/class/drm/card%d/dri_library_name", card);
115 f = fopen(path, "r");
116 if (!f)
117 return 0;
119 driver = malloc(sizeof(PATH_MAX));
120 driver = 0;
121 driver_len = 0;
122 len = getline(&driver, &driver_len, f); /* this is Linux-only so we must have glibc */
123 fclose(f);
125 if (len >= 1)
127 /* remove the trailing newline from sysfs */
128 driver[len - 1] = '\0';
129 return driver;
132 return 0;
135 static int
136 drmIoctl(int fd, unsigned long request, void *arg)
138 int ret;
140 do {
141 ret = ioctl(fd, request, arg);
142 } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
143 return ret;
146 static int
147 drm_open(int card)
149 char path[ NAME_MAX ];
150 int fd;
151 drm_set_version_t sv;
153 sprintf(path, "/dev/dri/card%d", card);
154 fd = open(path, O_RDWR, 0);
155 if(fd < 0)
156 return 0;
158 /* Set the interface version, asking for 1.2 */
159 sv.drm_di_major = 1;
160 sv.drm_di_minor = 2;
161 sv.drm_dd_major = -1;
162 sv.drm_dd_minor = -1;
164 if (drmIoctl(fd, DRM_IOCTL_SET_VERSION, &sv))
165 goto fail;
167 return fd;
169 fail:
170 close(fd);
171 return 0;
174 static cairo_space_t*
175 _cairo_gallium_drm_space_create (int fd, void* driver, struct drm_api* api, unsigned flags)
177 cairo_gpu_space_t* space;
178 struct pipe_screen* screen;
179 struct drm_create_screen_arg arg;
181 arg.mode = DRM_CREATE_NORMAL;
182 screen = api->create_screen(api, fd, &arg);
184 space = _cairo_gpu_space__begin_create();
185 if(!space)
187 screen->destroy(screen);
188 return 0;
191 space->api = API_DRM;
192 space->drm.driver = driver;
193 space->drm.fd = fd;
194 space->drm.api = api;
195 space->screen = screen;
196 space->owns_screen = 1;
198 _cairo_gpu_space__finish_create(space, flags);
199 return &space->base;
202 cairo_space_t*
203 cairo_gallium_drm_space_wrap(struct drm_api* api, struct pipe_screen* screen, unsigned flags)
205 cairo_gpu_space_t* space = _cairo_gpu_space__begin_create();
206 if(!space)
207 return 0;
209 space->api = API_DRM;
210 space->drm.api = api;
211 space->drm.fd = -1;
212 space->screen = screen;
213 space->owns_screen = 1;
215 _cairo_gpu_space__finish_create(space, flags);
216 return &space->base;
219 cairo_space_t*
220 cairo_gallium_drm_space_create (int card, unsigned flags)
222 cairo_space_t* space;
223 void* driver;
224 char* driver_name;
225 int fd;
227 struct drm_api* api;
228 drm_auth_t auth;
230 driver_name = drm_get_driver_name(0);
231 if(!driver_name)
232 return 0;
234 driver = drm_open_driver(driver_name, &api);
235 free(driver_name);
236 if(!driver)
237 return 0;
239 fd = drm_open(card);
240 if(fd < 0)
241 goto fail;
243 if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth))
244 goto fail2;
246 if (drmIoctl(fd, DRM_IOCTL_AUTH_MAGIC, &auth))
247 goto fail2;
249 space = _cairo_gallium_drm_space_create(fd, driver, api, flags);
250 if(space)
251 return space;
253 fail2:
254 close(fd);
255 fail:
256 dlclose(driver);
257 return 0;
260 static char dri2ExtensionName[] = DRI2_NAME;
261 static XExtensionInfo *dri2Info;
262 static XEXT_GENERATE_CLOSE_DISPLAY (DRI2CloseDisplay, dri2Info);
263 static /* const */ XExtensionHooks dri2ExtensionHooks = {
264 NULL, /* create_gc */
265 NULL, /* copy_gc */
266 NULL, /* flush_gc */
267 NULL, /* free_gc */
268 NULL, /* create_font */
269 NULL, /* free_font */
270 DRI2CloseDisplay, /* close_display */
271 NULL, /* wire_to_event */
272 NULL, /* event_to_wire */
273 NULL, /* error */
274 NULL, /* error_string */
277 static XEXT_GENERATE_FIND_DISPLAY (DRI2FindDisplay, dri2Info,
278 dri2ExtensionName,
279 &dri2ExtensionHooks,
280 0, NULL);
282 static Bool DRI2Connect(Display *dpy, XID window,
283 char **driverName, char **deviceName)
285 XExtDisplayInfo *info = DRI2FindDisplay(dpy);
286 xDRI2ConnectReply rep;
287 xDRI2ConnectReq *req;
289 XextCheckExtension (dpy, info, DRI2_NAME, False);
291 LockDisplay(dpy);
292 GetReq(DRI2Connect, req);
293 req->reqType = info->codes->major_opcode;
294 req->dri2ReqType = X_DRI2Connect;
295 req->window = window;
296 req->driverType = DRI2DriverDRI;
297 if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
298 UnlockDisplay(dpy);
299 SyncHandle();
300 return False;
303 if (rep.driverNameLength == 0 && rep.deviceNameLength == 0) {
304 UnlockDisplay(dpy);
305 SyncHandle();
306 return False;
309 *driverName = Xmalloc(rep.driverNameLength + 1);
310 if (*driverName == NULL) {
311 _XEatData(dpy,
312 ((rep.driverNameLength + 3) & ~3) +
313 ((rep.deviceNameLength + 3) & ~3));
314 UnlockDisplay(dpy);
315 SyncHandle();
316 return False;
318 _XReadPad(dpy, *driverName, rep.driverNameLength);
319 (*driverName)[rep.driverNameLength] = '\0';
321 *deviceName = Xmalloc(rep.deviceNameLength + 1);
322 if (*deviceName == NULL) {
323 Xfree(*driverName);
324 _XEatData(dpy, ((rep.deviceNameLength + 3) & ~3));
325 UnlockDisplay(dpy);
326 SyncHandle();
327 return False;
329 _XReadPad(dpy, *deviceName, rep.deviceNameLength);
330 (*deviceName)[rep.deviceNameLength] = '\0';
332 UnlockDisplay(dpy);
333 SyncHandle();
335 return True;
338 static Bool DRI2Authenticate(Display *dpy, XID window, drm_magic_t magic)
340 XExtDisplayInfo *info = DRI2FindDisplay(dpy);
341 xDRI2AuthenticateReq *req;
342 xDRI2AuthenticateReply rep;
344 XextCheckExtension (dpy, info, DRI2_NAME, False);
346 LockDisplay(dpy);
347 GetReq(DRI2Authenticate, req);
348 req->reqType = info->codes->major_opcode;
349 req->dri2ReqType = X_DRI2Authenticate;
350 req->window = window;
351 req->magic = magic;
353 if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
354 UnlockDisplay(dpy);
355 SyncHandle();
356 return False;
359 UnlockDisplay(dpy);
360 SyncHandle();
362 return rep.authenticated;
365 cairo_space_t*
366 cairo_gallium_x11_space_wrap (Display* dpy, int scr, cairo_bool_t owns_dpy, unsigned flags)
368 cairo_space_t* space;
369 char *driver_name, *device_name;
370 int fd;
371 void* driver;
372 struct drm_api* api;
373 drm_auth_t auth;
375 if (!DRI2Connect(dpy, RootWindow(dpy, scr), &driver_name, &device_name))
376 return 0;
378 driver = drm_open_driver(driver_name, &api);
379 XFree(driver_name);
380 if(!driver)
382 XFree(device_name);
383 return 0;
386 fd = open(device_name, O_RDWR);
387 XFree(device_name);
388 if(fd < 0)
389 goto fail;
391 if (drmIoctl(fd, DRM_IOCTL_GET_MAGIC, &auth))
392 goto fail2;
394 if(!DRI2Authenticate(dpy, RootWindow(dpy, scr), auth.magic))
395 goto fail2;
397 space = _cairo_gallium_drm_space_create(fd, driver, api, flags);
398 if(!space)
399 goto fail2;
401 if(owns_dpy)
402 ((cairo_gpu_space_t*)space)->drm.dpy = dpy;
403 return space;
405 fail2:
406 close(fd);
407 fail:
408 dlclose(driver);
409 return 0;
412 cairo_space_t*
413 cairo_gallium_x11_space_create (const char* name, unsigned flags)
415 cairo_space_t* space;
416 Display* dpy;
417 if(!XOpenDisplay)
418 return 0;
419 dpy = XOpenDisplay(name);
420 space = cairo_gallium_x11_space_wrap(dpy, DefaultScreen(dpy), 1, flags);
421 if(!space)
422 XCloseDisplay(dpy);
423 return space;
426 void
427 _cairo_gpu_drm_space_destroy(cairo_gpu_space_t* space)
429 if(space->drm.driver)
430 dlclose(space->drm.driver);
431 if(space->drm.fd >= 0)
432 close(space->drm.fd);
433 if(space->drm.dpy)
434 XCloseDisplay(space->drm.dpy);