intel_device.c revision 42542f5f
103b705cfSriastradh/***************************************************************************
203b705cfSriastradh
303b705cfSriastradh Copyright 2013 Intel Corporation.  All Rights Reserved.
403b705cfSriastradh
503b705cfSriastradh Permission is hereby granted, free of charge, to any person obtaining a
603b705cfSriastradh copy of this software and associated documentation files (the
703b705cfSriastradh "Software"), to deal in the Software without restriction, including
803b705cfSriastradh without limitation the rights to use, copy, modify, merge, publish,
903b705cfSriastradh distribute, sub license, and/or sell copies of the Software, and to
1003b705cfSriastradh permit persons to whom the Software is furnished to do so, subject to
1103b705cfSriastradh the following conditions:
1203b705cfSriastradh
1303b705cfSriastradh The above copyright notice and this permission notice (including the
1403b705cfSriastradh next paragraph) shall be included in all copies or substantial portions
1503b705cfSriastradh of the Software.
1603b705cfSriastradh
1703b705cfSriastradh THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1803b705cfSriastradh OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1903b705cfSriastradh MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
2003b705cfSriastradh IN NO EVENT SHALL INTEL, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
2103b705cfSriastradh DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
2203b705cfSriastradh OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
2303b705cfSriastradh THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2403b705cfSriastradh
2503b705cfSriastradh **************************************************************************/
2603b705cfSriastradh
2742542f5fSchristos#ifdef HAVE_CONFIG_H
2842542f5fSchristos#include "config.h"
2942542f5fSchristos#endif
3042542f5fSchristos
3142542f5fSchristos#include <sys/types.h>
3242542f5fSchristos#include <sys/stat.h>
3303b705cfSriastradh#include <assert.h>
3403b705cfSriastradh#include <string.h>
3503b705cfSriastradh#include <unistd.h>
3603b705cfSriastradh#include <fcntl.h>
3703b705cfSriastradh#include <stdlib.h>
3842542f5fSchristos#include <dirent.h>
3903b705cfSriastradh#include <errno.h>
4003b705cfSriastradh
4103b705cfSriastradh#include <pciaccess.h>
4242542f5fSchristos
4342542f5fSchristos#include <xorg-server.h>
4403b705cfSriastradh#include <xf86.h>
4503b705cfSriastradh#include <xf86drm.h>
4603b705cfSriastradh#include <xf86drmMode.h>
4703b705cfSriastradh#include <xf86_OSproc.h>
4803b705cfSriastradh#include <i915_drm.h>
4903b705cfSriastradh
5042542f5fSchristos#ifdef XSERVER_PLATFORM_BUS
5142542f5fSchristos#include <xf86platformBus.h>
5242542f5fSchristos#endif
5342542f5fSchristos
5442542f5fSchristos#ifdef HAVE_VALGRIND
5542542f5fSchristos#include <valgrind.h>
5642542f5fSchristos#include <memcheck.h>
5742542f5fSchristos#define VG(x) x
5842542f5fSchristos#else
5942542f5fSchristos#define VG(x)
6042542f5fSchristos#endif
6142542f5fSchristos
6242542f5fSchristos#define VG_CLEAR(s) VG(memset(&s, 0, sizeof(s)))
6342542f5fSchristos
6403b705cfSriastradh#include "intel_driver.h"
6542542f5fSchristos#include "fd.h"
6603b705cfSriastradh
6703b705cfSriastradhstruct intel_device {
6842542f5fSchristos	char *master_node;
6942542f5fSchristos	char *render_node;
7003b705cfSriastradh	int fd;
7103b705cfSriastradh	int open_count;
7203b705cfSriastradh	int master_count;
7303b705cfSriastradh};
7403b705cfSriastradh
7503b705cfSriastradhstatic int intel_device_key = -1;
7603b705cfSriastradh
7742542f5fSchristosstatic int dump_file(ScrnInfoPtr scrn, const char *path)
7842542f5fSchristos{
7942542f5fSchristos	FILE *file;
8042542f5fSchristos	size_t len = 0;
8142542f5fSchristos	char *line = NULL;
8242542f5fSchristos
8342542f5fSchristos	file = fopen(path, "r");
8442542f5fSchristos	if (file == NULL)
8542542f5fSchristos		return 0;
8642542f5fSchristos
8742542f5fSchristos	xf86DrvMsg(scrn->scrnIndex, X_INFO, "[drm] Contents of '%s':\n", path);
8842542f5fSchristos	while (getline(&line, &len, file) != -1)
8942542f5fSchristos		xf86DrvMsg(scrn->scrnIndex, X_INFO, "[drm] %s", line);
9042542f5fSchristos
9142542f5fSchristos	free(line);
9242542f5fSchristos	fclose(file);
9342542f5fSchristos	return 1;
9442542f5fSchristos}
9542542f5fSchristos
9642542f5fSchristosstatic int __find_debugfs(void)
9742542f5fSchristos{
9842542f5fSchristos	int i;
9942542f5fSchristos
10042542f5fSchristos	for (i = 0; i < DRM_MAX_MINOR; i++) {
10142542f5fSchristos		char path[80];
10242542f5fSchristos
10342542f5fSchristos		sprintf(path, "/sys/kernel/debug/dri/%d/i915_wedged", i);
10442542f5fSchristos		if (access(path, R_OK) == 0)
10542542f5fSchristos			return i;
10642542f5fSchristos
10742542f5fSchristos		sprintf(path, "/debug/dri/%d/i915_wedged", i);
10842542f5fSchristos		if (access(path, R_OK) == 0)
10942542f5fSchristos			return i;
11042542f5fSchristos	}
11142542f5fSchristos
11242542f5fSchristos	return -1;
11342542f5fSchristos}
11442542f5fSchristos
11542542f5fSchristosstatic int drm_get_minor(int fd)
11642542f5fSchristos{
11742542f5fSchristos	struct stat st;
11842542f5fSchristos
11942542f5fSchristos	if (fstat(fd, &st))
12042542f5fSchristos		return __find_debugfs();
12142542f5fSchristos
12242542f5fSchristos	if (!S_ISCHR(st.st_mode))
12342542f5fSchristos		return __find_debugfs();
12442542f5fSchristos
12542542f5fSchristos	return st.st_rdev & 0x63;
12642542f5fSchristos}
12742542f5fSchristos
12842542f5fSchristos#if __linux__
12942542f5fSchristos#include <sys/mount.h>
13042542f5fSchristos
13142542f5fSchristosstatic void dump_debugfs(ScrnInfoPtr scrn, int fd, const char *name)
13242542f5fSchristos{
13342542f5fSchristos	char path[80];
13442542f5fSchristos	int minor;
13542542f5fSchristos
13642542f5fSchristos	minor = drm_get_minor(fd);
13742542f5fSchristos	if (minor < 0)
13842542f5fSchristos		return;
13942542f5fSchristos
14042542f5fSchristos	sprintf(path, "/sys/kernel/debug/dri/%d/%s", minor, name);
14142542f5fSchristos	if (dump_file(scrn, path))
14242542f5fSchristos		return;
14342542f5fSchristos
14442542f5fSchristos	sprintf(path, "/debug/dri/%d/%s", minor, name);
14542542f5fSchristos	if (dump_file(scrn, path))
14642542f5fSchristos		return;
14742542f5fSchristos
14842542f5fSchristos	if (mount("X-debug", "/sys/kernel/debug", "debugfs", 0, 0) == 0) {
14942542f5fSchristos		sprintf(path, "/sys/kernel/debug/dri/%d/%s", minor, name);
15042542f5fSchristos		dump_file(scrn, path);
15142542f5fSchristos		umount("X-debug");
15242542f5fSchristos		return;
15342542f5fSchristos	}
15442542f5fSchristos}
15542542f5fSchristos#else
15642542f5fSchristosstatic void dump_debugfs(ScrnInfoPtr scrn, int fd, const char *name) { }
15742542f5fSchristos#endif
15842542f5fSchristos
15942542f5fSchristosstatic void dump_clients_info(ScrnInfoPtr scrn, int fd)
16042542f5fSchristos{
16142542f5fSchristos	dump_debugfs(scrn, fd, "clients");
16242542f5fSchristos}
16342542f5fSchristos
16442542f5fSchristosstatic int __intel_get_device_id(int fd)
16542542f5fSchristos{
16642542f5fSchristos	struct drm_i915_getparam gp;
16742542f5fSchristos	int devid = 0;
16842542f5fSchristos
16942542f5fSchristos	VG_CLEAR(gp);
17042542f5fSchristos	gp.param = I915_PARAM_CHIPSET_ID;
17142542f5fSchristos	gp.value = &devid;
17242542f5fSchristos
17342542f5fSchristos	if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
17442542f5fSchristos		return 0;
17542542f5fSchristos
17642542f5fSchristos	return devid;
17742542f5fSchristos}
17842542f5fSchristos
17942542f5fSchristosint intel_entity_get_devid(int idx)
18042542f5fSchristos{
18142542f5fSchristos	struct intel_device *dev;
18242542f5fSchristos
18342542f5fSchristos	dev = xf86GetEntityPrivate(idx, intel_device_key)->ptr;
18442542f5fSchristos	if (dev == NULL)
18542542f5fSchristos		return 0;
18642542f5fSchristos
18742542f5fSchristos	return __intel_get_device_id(dev->fd);
18842542f5fSchristos}
18942542f5fSchristos
19003b705cfSriastradhstatic inline struct intel_device *intel_device(ScrnInfoPtr scrn)
19103b705cfSriastradh{
19203b705cfSriastradh	if (scrn->entityList == NULL)
19303b705cfSriastradh		return NULL;
19403b705cfSriastradh
19503b705cfSriastradh	return xf86GetEntityPrivate(scrn->entityList[0], intel_device_key)->ptr;
19603b705cfSriastradh}
19703b705cfSriastradh
19803b705cfSriastradhstatic inline void intel_set_device(ScrnInfoPtr scrn, struct intel_device *dev)
19903b705cfSriastradh{
20003b705cfSriastradh	xf86GetEntityPrivate(scrn->entityList[0], intel_device_key)->ptr = dev;
20103b705cfSriastradh}
20203b705cfSriastradh
20342542f5fSchristosstatic int is_i915_device(int fd)
20403b705cfSriastradh{
20503b705cfSriastradh	drm_version_t version;
20603b705cfSriastradh	char name[5] = "";
20703b705cfSriastradh
20803b705cfSriastradh	memset(&version, 0, sizeof(version));
20903b705cfSriastradh	version.name_len = 4;
21003b705cfSriastradh	version.name = name;
21103b705cfSriastradh
21203b705cfSriastradh	if (drmIoctl(fd, DRM_IOCTL_VERSION, &version))
21342542f5fSchristos		return 0;
21403b705cfSriastradh
21503b705cfSriastradh	return strcmp("i915", name) == 0;
21603b705cfSriastradh}
21703b705cfSriastradh
21842542f5fSchristosstatic int is_i915_gem(int fd)
21903b705cfSriastradh{
22042542f5fSchristos	int ret = is_i915_device(fd);
22103b705cfSriastradh
22203b705cfSriastradh	if (ret) {
22303b705cfSriastradh		struct drm_i915_getparam gp;
22442542f5fSchristos
22542542f5fSchristos		VG_CLEAR(gp);
22603b705cfSriastradh		gp.param = I915_PARAM_HAS_GEM;
22703b705cfSriastradh		gp.value = &ret;
22842542f5fSchristos
22903b705cfSriastradh		if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
23042542f5fSchristos			ret = 0;
23103b705cfSriastradh	}
23242542f5fSchristos
23342542f5fSchristos	return ret;
23442542f5fSchristos}
23542542f5fSchristos
23642542f5fSchristosstatic int __intel_check_device(int fd)
23742542f5fSchristos{
23842542f5fSchristos	int ret;
23942542f5fSchristos
24042542f5fSchristos	/* Confirm that this is a i915.ko device with GEM/KMS enabled */
24142542f5fSchristos	ret = is_i915_gem(fd);
24203b705cfSriastradh	if (ret && !hosted()) {
24303b705cfSriastradh		struct drm_mode_card_res res;
24403b705cfSriastradh
24503b705cfSriastradh		memset(&res, 0, sizeof(res));
24603b705cfSriastradh		if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
24742542f5fSchristos			ret = 0;
24803b705cfSriastradh	}
24903b705cfSriastradh
25003b705cfSriastradh	return ret;
25103b705cfSriastradh}
25203b705cfSriastradh
25342542f5fSchristosstatic int open_cloexec(const char *path)
25403b705cfSriastradh{
25542542f5fSchristos	struct stat st;
25642542f5fSchristos	int loop = 1000;
25742542f5fSchristos	int fd;
25803b705cfSriastradh
25942542f5fSchristos	/* No file? Assume the driver is loading slowly */
26042542f5fSchristos	while (stat(path, &st) == -1 && errno == ENOENT && --loop)
26142542f5fSchristos		usleep(50000);
26242542f5fSchristos
26342542f5fSchristos	if (loop != 1000)
26442542f5fSchristos		ErrorF("intel: waited %d ms for '%s' to appear\n",
26542542f5fSchristos		       (1000 - loop) * 50, path);
26642542f5fSchristos
26742542f5fSchristos	fd = -1;
26842542f5fSchristos#ifdef O_CLOEXEC
26942542f5fSchristos	fd = open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC);
27042542f5fSchristos#endif
27103b705cfSriastradh	if (fd == -1)
27242542f5fSchristos		fd = fd_set_cloexec(open(path, O_RDWR | O_NONBLOCK));
27303b705cfSriastradh
27442542f5fSchristos	return fd;
27542542f5fSchristos}
27642542f5fSchristos
27742542f5fSchristos#ifdef __linux__
27842542f5fSchristosstatic int __intel_open_device__major_minor(int _major, int _minor)
27942542f5fSchristos{
28042542f5fSchristos	char path[256];
28142542f5fSchristos	DIR *dir;
28242542f5fSchristos	struct dirent *de;
28342542f5fSchristos	int base, fd = -1;
28442542f5fSchristos
28542542f5fSchristos	base = sprintf(path, "/dev/dri/");
28642542f5fSchristos
28742542f5fSchristos	dir = opendir(path);
28842542f5fSchristos	if (dir == NULL)
28942542f5fSchristos		return -1;
29042542f5fSchristos
29142542f5fSchristos	while ((de = readdir(dir)) != NULL) {
29242542f5fSchristos		struct stat st;
29342542f5fSchristos
29442542f5fSchristos		if (*de->d_name == '.')
29542542f5fSchristos			continue;
29642542f5fSchristos
29742542f5fSchristos		sprintf(path + base, "%s", de->d_name);
29842542f5fSchristos		if (stat(path, &st) == 0 &&
29942542f5fSchristos		    major(st.st_rdev) == _major &&
30042542f5fSchristos		    minor(st.st_rdev) == _minor) {
30142542f5fSchristos			fd = open_cloexec(path);
30242542f5fSchristos			break;
30342542f5fSchristos		}
30403b705cfSriastradh	}
30542542f5fSchristos
30642542f5fSchristos	closedir(dir);
30703b705cfSriastradh
30803b705cfSriastradh	return fd;
30903b705cfSriastradh}
31003b705cfSriastradh
31142542f5fSchristosstatic int __intel_open_device__pci(const struct pci_device *pci)
31203b705cfSriastradh{
31342542f5fSchristos	struct stat st;
31442542f5fSchristos	char path[256];
31542542f5fSchristos	DIR *dir;
31642542f5fSchristos	struct dirent *de;
31742542f5fSchristos	int base;
31803b705cfSriastradh	int fd;
31903b705cfSriastradh
32042542f5fSchristos	/* Look up the major:minor for the drm device through sysfs.
32142542f5fSchristos	 * First we need to check that sysfs is available, then
32242542f5fSchristos	 * check that we have loaded our driver. When we are happy
32342542f5fSchristos	 * that our KMS module is loaded, we can then search for our
32442542f5fSchristos	 * device node. We make the assumption that it uses the same
32542542f5fSchristos	 * name, but after that we read the major:minor assigned to us
32642542f5fSchristos	 * and search for a matching entry in /dev.
32742542f5fSchristos	 */
32842542f5fSchristos
32942542f5fSchristos	base = sprintf(path,
33042542f5fSchristos		       "/sys/bus/pci/devices/%04x:%02x:%02x.%d/",
33142542f5fSchristos		       pci->domain, pci->bus, pci->dev, pci->func);
33242542f5fSchristos	if (stat(path, &st))
33342542f5fSchristos		return -1;
33403b705cfSriastradh
33542542f5fSchristos	sprintf(path + base, "drm");
33642542f5fSchristos	dir = opendir(path);
33742542f5fSchristos	if (dir == NULL) {
33842542f5fSchristos		int loop = 0;
33903b705cfSriastradh
34042542f5fSchristos		sprintf(path + base, "driver");
34142542f5fSchristos		if (stat(path, &st)) {
34203b705cfSriastradh			if (xf86LoadKernelModule("i915"))
34303b705cfSriastradh				return -1;
34403b705cfSriastradh			(void)xf86LoadKernelModule("fbcon");
34503b705cfSriastradh		}
34603b705cfSriastradh
34742542f5fSchristos		sprintf(path + base, "drm");
34842542f5fSchristos		while ((dir = opendir(path)) == NULL && loop++ < 100)
34942542f5fSchristos			usleep(20000);
35042542f5fSchristos
35142542f5fSchristos		ErrorF("intel: waited %d ms for i915.ko driver to load\n", loop * 20000 / 1000);
35242542f5fSchristos
35342542f5fSchristos		if (dir == NULL)
35442542f5fSchristos			return -1;
35542542f5fSchristos	}
35642542f5fSchristos
35742542f5fSchristos	fd = -1;
35842542f5fSchristos	while ((de = readdir(dir)) != NULL) {
35942542f5fSchristos		if (*de->d_name == '.')
36042542f5fSchristos			continue;
36142542f5fSchristos
36242542f5fSchristos		if (strncmp(de->d_name, "card", 4) == 0) {
36342542f5fSchristos			sprintf(path + base + 4, "/dev/dri/%s", de->d_name);
36442542f5fSchristos			fd = open_cloexec(path + base + 4);
36542542f5fSchristos			if (fd != -1)
36642542f5fSchristos				break;
36742542f5fSchristos
36842542f5fSchristos			sprintf(path + base + 3, "/%s/dev", de->d_name);
36942542f5fSchristos			fd = open(path, O_RDONLY);
37042542f5fSchristos			if (fd == -1)
37142542f5fSchristos				break;
37242542f5fSchristos
37342542f5fSchristos			base = read(fd, path, sizeof(path) - 1);
37442542f5fSchristos			close(fd);
37542542f5fSchristos
37642542f5fSchristos			fd = -1;
37742542f5fSchristos			if (base > 0) {
37842542f5fSchristos				int major, minor;
37942542f5fSchristos				path[base] = '\0';
38042542f5fSchristos				if (sscanf(path, "%d:%d", &major, &minor) == 2)
38142542f5fSchristos					fd = __intel_open_device__major_minor(major, minor);
38203b705cfSriastradh			}
38342542f5fSchristos			break;
38403b705cfSriastradh		}
38542542f5fSchristos	}
38642542f5fSchristos	closedir(dir);
38742542f5fSchristos
38842542f5fSchristos	return fd;
38942542f5fSchristos}
39003b705cfSriastradh#else
39142542f5fSchristosstatic int __intel_open_device__pci(const struct pci_device *pci) { return -1; }
39203b705cfSriastradh#endif
39342542f5fSchristos
39442542f5fSchristosstatic int __intel_open_device__legacy(const struct pci_device *pci)
39542542f5fSchristos{
39642542f5fSchristos	char id[20];
39742542f5fSchristos	int ret;
39842542f5fSchristos
39942542f5fSchristos	snprintf(id, sizeof(id),
40042542f5fSchristos		 "pci:%04x:%02x:%02x.%d",
40142542f5fSchristos		 pci->domain, pci->bus, pci->dev, pci->func);
40242542f5fSchristos
40342542f5fSchristos	ret = drmCheckModesettingSupported(id);
40442542f5fSchristos	if (ret) {
40542542f5fSchristos		if (xf86LoadKernelModule("i915"))
40642542f5fSchristos			ret = drmCheckModesettingSupported(id);
40742542f5fSchristos		if (ret)
40842542f5fSchristos			return -1;
40942542f5fSchristos		/* Be nice to the user and load fbcon too */
41042542f5fSchristos		(void)xf86LoadKernelModule("fbcon");
41103b705cfSriastradh	}
41203b705cfSriastradh
41342542f5fSchristos	return fd_set_nonblock(drmOpen(NULL, id));
41442542f5fSchristos}
41542542f5fSchristos
41642542f5fSchristosstatic int __intel_open_device(const struct pci_device *pci, const char *path)
41742542f5fSchristos{
41842542f5fSchristos	int fd;
41942542f5fSchristos
42042542f5fSchristos	if (path == NULL) {
42142542f5fSchristos		if (pci == NULL)
42242542f5fSchristos			return -1;
42342542f5fSchristos
42442542f5fSchristos		fd = __intel_open_device__pci(pci);
42542542f5fSchristos		if (fd == -1)
42642542f5fSchristos			fd = __intel_open_device__legacy(pci);
42742542f5fSchristos	} else
42842542f5fSchristos		fd = open_cloexec(path);
42942542f5fSchristos
43003b705cfSriastradh	return fd;
43103b705cfSriastradh}
43203b705cfSriastradh
43342542f5fSchristosstatic char *find_master_node(int fd)
43442542f5fSchristos{
43542542f5fSchristos	struct stat st, master;
43642542f5fSchristos	char buf[128];
43742542f5fSchristos
43842542f5fSchristos	if (fstat(fd, &st))
43942542f5fSchristos		return NULL;
44042542f5fSchristos
44142542f5fSchristos	if (!S_ISCHR(st.st_mode))
44242542f5fSchristos		return NULL;
44342542f5fSchristos
44442542f5fSchristos	sprintf(buf, "/dev/dri/card%d", (int)(st.st_rdev & 0x7f));
44542542f5fSchristos	if (stat(buf, &master) == 0 &&
44642542f5fSchristos	    st.st_mode == master.st_mode &&
44742542f5fSchristos	    (st.st_rdev & 0x7f) == master.st_rdev)
44842542f5fSchristos		return strdup(buf);
44942542f5fSchristos
45042542f5fSchristos	/* Fallback to iterating over the usual suspects */
45142542f5fSchristos	return drmGetDeviceNameFromFd(fd);
45242542f5fSchristos}
45342542f5fSchristos
45442542f5fSchristosstatic int is_render_node(int fd, struct stat *st)
45542542f5fSchristos{
45642542f5fSchristos	if (fstat(fd, st))
45742542f5fSchristos		return 0;
45842542f5fSchristos
45942542f5fSchristos	if (!S_ISCHR(st->st_mode))
46042542f5fSchristos		return 0;
46142542f5fSchristos
46242542f5fSchristos	return st->st_rdev & 0x80;
46342542f5fSchristos}
46442542f5fSchristos
46542542f5fSchristosstatic char *find_render_node(int fd)
46642542f5fSchristos{
46742542f5fSchristos#if defined(USE_RENDERNODE)
46842542f5fSchristos	struct stat master, render;
46942542f5fSchristos	char buf[128];
47042542f5fSchristos
47142542f5fSchristos	/* Are we a render-node ourselves? */
47242542f5fSchristos	if (is_render_node(fd, &master))
47342542f5fSchristos		return NULL;
47442542f5fSchristos
47542542f5fSchristos	sprintf(buf, "/dev/dri/renderD%d", (int)((master.st_rdev | 0x80) & 0xbf));
47642542f5fSchristos	if (stat(buf, &render) == 0 &&
47742542f5fSchristos	    master.st_mode == render.st_mode &&
47842542f5fSchristos	    render.st_rdev == ((master.st_rdev | 0x80) & 0xbf))
47942542f5fSchristos		return strdup(buf);
48042542f5fSchristos#endif
48142542f5fSchristos
48242542f5fSchristos	return NULL;
48342542f5fSchristos}
48442542f5fSchristos
48542542f5fSchristos#if defined(ODEV_ATTRIB_PATH)
48642542f5fSchristosstatic char *get_path(struct xf86_platform_device *dev)
48742542f5fSchristos{
48842542f5fSchristos	const char *path;
48942542f5fSchristos
49042542f5fSchristos	if (dev == NULL)
49142542f5fSchristos		return NULL;
49242542f5fSchristos
49342542f5fSchristos	path = xf86_get_platform_device_attrib(dev, ODEV_ATTRIB_PATH);
49442542f5fSchristos	if (path == NULL)
49542542f5fSchristos		return NULL;
49642542f5fSchristos
49742542f5fSchristos	return strdup(path);
49842542f5fSchristos}
49942542f5fSchristos
50042542f5fSchristos#else
50142542f5fSchristos
50242542f5fSchristosstatic char *get_path(struct xf86_platform_device *dev)
50342542f5fSchristos{
50442542f5fSchristos	return NULL;
50542542f5fSchristos}
50642542f5fSchristos#endif
50742542f5fSchristos
50842542f5fSchristos
50942542f5fSchristos#if defined(ODEV_ATTRIB_FD)
51042542f5fSchristosstatic int get_fd(struct xf86_platform_device *dev)
51142542f5fSchristos{
51242542f5fSchristos	if (dev == NULL)
51342542f5fSchristos		return -1;
51442542f5fSchristos
51542542f5fSchristos	return xf86_get_platform_device_int_attrib(dev, ODEV_ATTRIB_FD, -1);
51642542f5fSchristos}
51742542f5fSchristos
51842542f5fSchristos#else
51942542f5fSchristos
52042542f5fSchristosstatic int get_fd(struct xf86_platform_device *dev)
52142542f5fSchristos{
52242542f5fSchristos	return -1;
52342542f5fSchristos}
52442542f5fSchristos#endif
52542542f5fSchristos
52642542f5fSchristosstatic int is_master(int fd)
52742542f5fSchristos{
52842542f5fSchristos	drmSetVersion sv;
52942542f5fSchristos
53042542f5fSchristos	sv.drm_di_major = 1;
53142542f5fSchristos	sv.drm_di_minor = 1;
53242542f5fSchristos	sv.drm_dd_major = -1;
53342542f5fSchristos	sv.drm_dd_minor = -1;
53442542f5fSchristos
53542542f5fSchristos	return drmIoctl(fd, DRM_IOCTL_SET_VERSION, &sv) == 0;
53642542f5fSchristos}
53742542f5fSchristos
53803b705cfSriastradhint intel_open_device(int entity_num,
53903b705cfSriastradh		      const struct pci_device *pci,
54042542f5fSchristos		      struct xf86_platform_device *platform)
54103b705cfSriastradh{
54203b705cfSriastradh	struct intel_device *dev;
54342542f5fSchristos	char *path;
54442542f5fSchristos	int fd, master_count;
54503b705cfSriastradh
54603b705cfSriastradh	if (intel_device_key == -1)
54703b705cfSriastradh		intel_device_key = xf86AllocateEntityPrivateIndex();
54803b705cfSriastradh	if (intel_device_key == -1)
54903b705cfSriastradh		return -1;
55003b705cfSriastradh
55103b705cfSriastradh	dev = xf86GetEntityPrivate(entity_num, intel_device_key)->ptr;
55203b705cfSriastradh	if (dev)
55303b705cfSriastradh		return dev->fd;
55403b705cfSriastradh
55542542f5fSchristos	path = get_path(platform);
55603b705cfSriastradh
55742542f5fSchristos	master_count = 1; /* DRM_MASTER is managed by Xserver */
55842542f5fSchristos	fd = get_fd(platform);
55942542f5fSchristos	if (fd == -1) {
56042542f5fSchristos		fd = __intel_open_device(pci, path);
56142542f5fSchristos		if (fd == -1)
56242542f5fSchristos			goto err_path;
56342542f5fSchristos
56442542f5fSchristos		master_count = 0;
56542542f5fSchristos	}
56642542f5fSchristos
56742542f5fSchristos	if (path == NULL) {
56842542f5fSchristos		path = find_master_node(fd);
56942542f5fSchristos		if (path == NULL)
57042542f5fSchristos			goto err_close;
57142542f5fSchristos	}
57203b705cfSriastradh
57303b705cfSriastradh	if (!__intel_check_device(fd))
57403b705cfSriastradh		goto err_close;
57503b705cfSriastradh
57603b705cfSriastradh	dev = malloc(sizeof(*dev));
57703b705cfSriastradh	if (dev == NULL)
57803b705cfSriastradh		goto err_close;
57903b705cfSriastradh
58003b705cfSriastradh	/* If hosted under a system compositor, just pretend to be master */
58142542f5fSchristos	if (hosted())
58242542f5fSchristos		master_count++;
58342542f5fSchristos
58442542f5fSchristos	/* Non-root user holding MASTER, don't let go */
58542542f5fSchristos	if (geteuid() && is_master(fd))
58642542f5fSchristos		master_count++;
58742542f5fSchristos
58842542f5fSchristos	dev->fd = fd;
58942542f5fSchristos	dev->open_count = master_count;
59042542f5fSchristos	dev->master_count = master_count;
59142542f5fSchristos	dev->master_node = path;
59242542f5fSchristos	dev->render_node = find_render_node(fd);
59342542f5fSchristos	if (dev->render_node == NULL)
59442542f5fSchristos		dev->render_node = dev->master_node;
59503b705cfSriastradh
59603b705cfSriastradh	xf86GetEntityPrivate(entity_num, intel_device_key)->ptr = dev;
59703b705cfSriastradh
59803b705cfSriastradh	return fd;
59903b705cfSriastradh
60003b705cfSriastradherr_close:
60142542f5fSchristos	if (master_count == 0) /* Don't close server-fds */
60242542f5fSchristos		close(fd);
60303b705cfSriastradherr_path:
60442542f5fSchristos	free(path);
60503b705cfSriastradh	return -1;
60603b705cfSriastradh}
60703b705cfSriastradh
60842542f5fSchristosint __intel_peek_fd(ScrnInfoPtr scrn)
60942542f5fSchristos{
61042542f5fSchristos	struct intel_device *dev;
61142542f5fSchristos
61242542f5fSchristos	dev = intel_device(scrn);
61342542f5fSchristos	assert(dev && dev->fd != -1);
61442542f5fSchristos
61542542f5fSchristos	return dev->fd;
61642542f5fSchristos}
61742542f5fSchristos
61842542f5fSchristosint intel_has_render_node(ScrnInfoPtr scrn)
61942542f5fSchristos{
62042542f5fSchristos	struct intel_device *dev;
62142542f5fSchristos	struct stat st;
62242542f5fSchristos
62342542f5fSchristos	dev = intel_device(scrn);
62442542f5fSchristos	assert(dev && dev->fd != -1);
62542542f5fSchristos
62642542f5fSchristos	return is_render_node(dev->fd, &st);
62742542f5fSchristos}
62842542f5fSchristos
62903b705cfSriastradhint intel_get_device(ScrnInfoPtr scrn)
63003b705cfSriastradh{
63103b705cfSriastradh	struct intel_device *dev;
63203b705cfSriastradh	int ret;
63303b705cfSriastradh
63403b705cfSriastradh	dev = intel_device(scrn);
63503b705cfSriastradh	assert(dev && dev->fd != -1);
63603b705cfSriastradh
63703b705cfSriastradh	if (dev->open_count++ == 0) {
63803b705cfSriastradh		drmSetVersion sv;
63903b705cfSriastradh		int retry = 2000;
64003b705cfSriastradh
64103b705cfSriastradh		assert(!hosted());
64203b705cfSriastradh
64303b705cfSriastradh		/* Check that what we opened was a master or a
64403b705cfSriastradh		 * master-capable FD, by setting the version of the
64503b705cfSriastradh		 * interface we'll use to talk to it.
64603b705cfSriastradh		 */
64703b705cfSriastradh		do {
64803b705cfSriastradh			sv.drm_di_major = 1;
64903b705cfSriastradh			sv.drm_di_minor = 1;
65003b705cfSriastradh			sv.drm_dd_major = -1;
65103b705cfSriastradh			sv.drm_dd_minor = -1;
65203b705cfSriastradh			ret = drmIoctl(dev->fd, DRM_IOCTL_SET_VERSION, &sv);
65303b705cfSriastradh			if (ret == 0)
65403b705cfSriastradh				break;
65503b705cfSriastradh
65603b705cfSriastradh			usleep(1000);
65703b705cfSriastradh		} while (--retry);
65803b705cfSriastradh		if (ret != 0) {
65903b705cfSriastradh			xf86DrvMsg(scrn->scrnIndex, X_ERROR,
66003b705cfSriastradh				   "[drm] failed to set drm interface version: %s [%d].\n",
66103b705cfSriastradh				   strerror(errno), errno);
66242542f5fSchristos			dump_clients_info(scrn, dev->fd);
66303b705cfSriastradh			dev->open_count--;
66403b705cfSriastradh			return -1;
66503b705cfSriastradh		}
66603b705cfSriastradh	}
66703b705cfSriastradh
66803b705cfSriastradh	return dev->fd;
66903b705cfSriastradh}
67003b705cfSriastradh
67142542f5fSchristosconst char *intel_get_client_name(ScrnInfoPtr scrn)
67242542f5fSchristos{
67342542f5fSchristos	struct intel_device *dev = intel_device(scrn);
67442542f5fSchristos	assert(dev && dev->render_node);
67542542f5fSchristos	return dev->render_node;
67642542f5fSchristos}
67742542f5fSchristos
67842542f5fSchristosstatic int authorise(struct intel_device *dev, int fd)
67942542f5fSchristos{
68042542f5fSchristos	struct stat st;
68142542f5fSchristos	drm_magic_t magic;
68242542f5fSchristos
68342542f5fSchristos	if (is_render_node(fd, &st)) /* restricted authority, do not elevate */
68442542f5fSchristos		return 1;
68542542f5fSchristos
68642542f5fSchristos	return drmGetMagic(fd, &magic) == 0 && drmAuthMagic(dev->fd, magic) == 0;
68742542f5fSchristos}
68842542f5fSchristos
68942542f5fSchristosint intel_get_client_fd(ScrnInfoPtr scrn)
69042542f5fSchristos{
69142542f5fSchristos	struct intel_device *dev;
69242542f5fSchristos	int fd = -1;
69342542f5fSchristos
69442542f5fSchristos	dev = intel_device(scrn);
69542542f5fSchristos	assert(dev);
69642542f5fSchristos	assert(dev->fd != -1);
69742542f5fSchristos	assert(dev->render_node);
69842542f5fSchristos
69942542f5fSchristos#ifdef O_CLOEXEC
70042542f5fSchristos	fd = open(dev->render_node, O_RDWR | O_CLOEXEC);
70142542f5fSchristos#endif
70242542f5fSchristos	if (fd < 0)
70342542f5fSchristos		fd = fd_set_cloexec(open(dev->render_node, O_RDWR));
70442542f5fSchristos	if (fd < 0)
70542542f5fSchristos		return -BadAlloc;
70642542f5fSchristos
70742542f5fSchristos	if (!authorise(dev, fd)) {
70842542f5fSchristos		close(fd);
70942542f5fSchristos		return -BadMatch;
71042542f5fSchristos	}
71142542f5fSchristos
71242542f5fSchristos	assert(is_i915_gem(fd));
71342542f5fSchristos
71442542f5fSchristos	return fd;
71542542f5fSchristos}
71642542f5fSchristos
71742542f5fSchristosint intel_get_device_id(ScrnInfoPtr scrn)
71803b705cfSriastradh{
71903b705cfSriastradh	struct intel_device *dev = intel_device(scrn);
72042542f5fSchristos	assert(dev && dev->fd != -1);
72142542f5fSchristos	return __intel_get_device_id(dev->fd);
72203b705cfSriastradh}
72303b705cfSriastradh
72403b705cfSriastradhint intel_get_master(ScrnInfoPtr scrn)
72503b705cfSriastradh{
72603b705cfSriastradh	struct intel_device *dev = intel_device(scrn);
72703b705cfSriastradh	int ret;
72803b705cfSriastradh
72903b705cfSriastradh	assert(dev && dev->fd != -1);
73003b705cfSriastradh
73103b705cfSriastradh	ret = 0;
73203b705cfSriastradh	if (dev->master_count++ == 0) {
73303b705cfSriastradh		int retry = 2000;
73403b705cfSriastradh
73503b705cfSriastradh		assert(!hosted());
73603b705cfSriastradh		do {
73703b705cfSriastradh			ret = drmSetMaster(dev->fd);
73803b705cfSriastradh			if (ret == 0)
73903b705cfSriastradh				break;
74003b705cfSriastradh			usleep(1000);
74103b705cfSriastradh		} while (--retry);
74203b705cfSriastradh	}
74303b705cfSriastradh
74403b705cfSriastradh	return ret;
74503b705cfSriastradh}
74603b705cfSriastradh
74703b705cfSriastradhint intel_put_master(ScrnInfoPtr scrn)
74803b705cfSriastradh{
74903b705cfSriastradh	struct intel_device *dev = intel_device(scrn);
75003b705cfSriastradh	int ret;
75103b705cfSriastradh
75203b705cfSriastradh	assert(dev && dev->fd != -1);
75303b705cfSriastradh
75403b705cfSriastradh	ret = 0;
75503b705cfSriastradh	assert(dev->master_count);
75603b705cfSriastradh	if (--dev->master_count == 0) {
75703b705cfSriastradh		assert(!hosted());
75803b705cfSriastradh		assert(drmSetMaster(dev->fd) == 0);
75903b705cfSriastradh		ret = drmDropMaster(dev->fd);
76003b705cfSriastradh	}
76103b705cfSriastradh
76203b705cfSriastradh	return ret;
76303b705cfSriastradh}
76403b705cfSriastradh
76503b705cfSriastradhvoid intel_put_device(ScrnInfoPtr scrn)
76603b705cfSriastradh{
76703b705cfSriastradh	struct intel_device *dev = intel_device(scrn);
76803b705cfSriastradh
76903b705cfSriastradh	assert(dev && dev->fd != -1);
77003b705cfSriastradh
77103b705cfSriastradh	assert(dev->open_count);
77203b705cfSriastradh	if (--dev->open_count)
77303b705cfSriastradh		return;
77403b705cfSriastradh
77503b705cfSriastradh	assert(!hosted());
77603b705cfSriastradh	intel_set_device(scrn, NULL);
77703b705cfSriastradh
77803b705cfSriastradh	drmClose(dev->fd);
77942542f5fSchristos	if (dev->render_node != dev->master_node)
78042542f5fSchristos		free(dev->render_node);
78142542f5fSchristos	free(dev->master_node);
78203b705cfSriastradh	free(dev);
78303b705cfSriastradh}
784