intel_device.c revision 13496ba1
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 {
6813496ba1Ssnj	int idx;
6942542f5fSchristos	char *master_node;
7042542f5fSchristos	char *render_node;
7103b705cfSriastradh	int fd;
7213496ba1Ssnj	int device_id;
7303b705cfSriastradh	int open_count;
7403b705cfSriastradh	int master_count;
7503b705cfSriastradh};
7603b705cfSriastradh
7703b705cfSriastradhstatic int intel_device_key = -1;
7803b705cfSriastradh
7942542f5fSchristosstatic int dump_file(ScrnInfoPtr scrn, const char *path)
8042542f5fSchristos{
8142542f5fSchristos	FILE *file;
8242542f5fSchristos	size_t len = 0;
8342542f5fSchristos	char *line = NULL;
8442542f5fSchristos
8542542f5fSchristos	file = fopen(path, "r");
8642542f5fSchristos	if (file == NULL)
8742542f5fSchristos		return 0;
8842542f5fSchristos
8942542f5fSchristos	xf86DrvMsg(scrn->scrnIndex, X_INFO, "[drm] Contents of '%s':\n", path);
9042542f5fSchristos	while (getline(&line, &len, file) != -1)
9142542f5fSchristos		xf86DrvMsg(scrn->scrnIndex, X_INFO, "[drm] %s", line);
9242542f5fSchristos
9342542f5fSchristos	free(line);
9442542f5fSchristos	fclose(file);
9542542f5fSchristos	return 1;
9642542f5fSchristos}
9742542f5fSchristos
9842542f5fSchristosstatic int __find_debugfs(void)
9942542f5fSchristos{
10042542f5fSchristos	int i;
10142542f5fSchristos
10242542f5fSchristos	for (i = 0; i < DRM_MAX_MINOR; i++) {
10342542f5fSchristos		char path[80];
10442542f5fSchristos
10542542f5fSchristos		sprintf(path, "/sys/kernel/debug/dri/%d/i915_wedged", i);
10642542f5fSchristos		if (access(path, R_OK) == 0)
10742542f5fSchristos			return i;
10842542f5fSchristos
10942542f5fSchristos		sprintf(path, "/debug/dri/%d/i915_wedged", i);
11042542f5fSchristos		if (access(path, R_OK) == 0)
11142542f5fSchristos			return i;
11242542f5fSchristos	}
11342542f5fSchristos
11442542f5fSchristos	return -1;
11542542f5fSchristos}
11642542f5fSchristos
11742542f5fSchristosstatic int drm_get_minor(int fd)
11842542f5fSchristos{
11942542f5fSchristos	struct stat st;
12042542f5fSchristos
12142542f5fSchristos	if (fstat(fd, &st))
12242542f5fSchristos		return __find_debugfs();
12342542f5fSchristos
12442542f5fSchristos	if (!S_ISCHR(st.st_mode))
12542542f5fSchristos		return __find_debugfs();
12642542f5fSchristos
12742542f5fSchristos	return st.st_rdev & 0x63;
12842542f5fSchristos}
12942542f5fSchristos
13042542f5fSchristos#if __linux__
13142542f5fSchristos#include <sys/mount.h>
13242542f5fSchristos
13342542f5fSchristosstatic void dump_debugfs(ScrnInfoPtr scrn, int fd, const char *name)
13442542f5fSchristos{
13542542f5fSchristos	char path[80];
13642542f5fSchristos	int minor;
13742542f5fSchristos
13842542f5fSchristos	minor = drm_get_minor(fd);
13942542f5fSchristos	if (minor < 0)
14042542f5fSchristos		return;
14142542f5fSchristos
14242542f5fSchristos	sprintf(path, "/sys/kernel/debug/dri/%d/%s", minor, name);
14342542f5fSchristos	if (dump_file(scrn, path))
14442542f5fSchristos		return;
14542542f5fSchristos
14642542f5fSchristos	sprintf(path, "/debug/dri/%d/%s", minor, name);
14742542f5fSchristos	if (dump_file(scrn, path))
14842542f5fSchristos		return;
14942542f5fSchristos
15042542f5fSchristos	if (mount("X-debug", "/sys/kernel/debug", "debugfs", 0, 0) == 0) {
15142542f5fSchristos		sprintf(path, "/sys/kernel/debug/dri/%d/%s", minor, name);
15242542f5fSchristos		dump_file(scrn, path);
15342542f5fSchristos		umount("X-debug");
15442542f5fSchristos		return;
15542542f5fSchristos	}
15642542f5fSchristos}
15742542f5fSchristos#else
15842542f5fSchristosstatic void dump_debugfs(ScrnInfoPtr scrn, int fd, const char *name) { }
15942542f5fSchristos#endif
16042542f5fSchristos
16142542f5fSchristosstatic void dump_clients_info(ScrnInfoPtr scrn, int fd)
16242542f5fSchristos{
16342542f5fSchristos	dump_debugfs(scrn, fd, "clients");
16442542f5fSchristos}
16542542f5fSchristos
16642542f5fSchristosstatic int __intel_get_device_id(int fd)
16742542f5fSchristos{
16842542f5fSchristos	struct drm_i915_getparam gp;
16942542f5fSchristos	int devid = 0;
17042542f5fSchristos
17142542f5fSchristos	VG_CLEAR(gp);
17242542f5fSchristos	gp.param = I915_PARAM_CHIPSET_ID;
17342542f5fSchristos	gp.value = &devid;
17442542f5fSchristos
17542542f5fSchristos	if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
17642542f5fSchristos		return 0;
17742542f5fSchristos
17842542f5fSchristos	return devid;
17942542f5fSchristos}
18042542f5fSchristos
18142542f5fSchristosint intel_entity_get_devid(int idx)
18242542f5fSchristos{
18342542f5fSchristos	struct intel_device *dev;
18442542f5fSchristos
18542542f5fSchristos	dev = xf86GetEntityPrivate(idx, intel_device_key)->ptr;
18642542f5fSchristos	if (dev == NULL)
18742542f5fSchristos		return 0;
18842542f5fSchristos
18913496ba1Ssnj	return dev->device_id;
19042542f5fSchristos}
19142542f5fSchristos
19203b705cfSriastradhstatic inline struct intel_device *intel_device(ScrnInfoPtr scrn)
19303b705cfSriastradh{
19403b705cfSriastradh	if (scrn->entityList == NULL)
19503b705cfSriastradh		return NULL;
19603b705cfSriastradh
19703b705cfSriastradh	return xf86GetEntityPrivate(scrn->entityList[0], intel_device_key)->ptr;
19803b705cfSriastradh}
19903b705cfSriastradh
20042542f5fSchristosstatic int is_i915_device(int fd)
20103b705cfSriastradh{
20203b705cfSriastradh	drm_version_t version;
20303b705cfSriastradh	char name[5] = "";
20403b705cfSriastradh
20503b705cfSriastradh	memset(&version, 0, sizeof(version));
20603b705cfSriastradh	version.name_len = 4;
20703b705cfSriastradh	version.name = name;
20803b705cfSriastradh
20903b705cfSriastradh	if (drmIoctl(fd, DRM_IOCTL_VERSION, &version))
21042542f5fSchristos		return 0;
21103b705cfSriastradh
21203b705cfSriastradh	return strcmp("i915", name) == 0;
21303b705cfSriastradh}
21403b705cfSriastradh
21542542f5fSchristosstatic int is_i915_gem(int fd)
21603b705cfSriastradh{
21742542f5fSchristos	int ret = is_i915_device(fd);
21803b705cfSriastradh
21903b705cfSriastradh	if (ret) {
22003b705cfSriastradh		struct drm_i915_getparam gp;
22142542f5fSchristos
22242542f5fSchristos		VG_CLEAR(gp);
22303b705cfSriastradh		gp.param = I915_PARAM_HAS_GEM;
22403b705cfSriastradh		gp.value = &ret;
22542542f5fSchristos
22603b705cfSriastradh		if (drmIoctl(fd, DRM_IOCTL_I915_GETPARAM, &gp))
22742542f5fSchristos			ret = 0;
22803b705cfSriastradh	}
22942542f5fSchristos
23042542f5fSchristos	return ret;
23142542f5fSchristos}
23242542f5fSchristos
23342542f5fSchristosstatic int __intel_check_device(int fd)
23442542f5fSchristos{
23542542f5fSchristos	int ret;
23642542f5fSchristos
23742542f5fSchristos	/* Confirm that this is a i915.ko device with GEM/KMS enabled */
23842542f5fSchristos	ret = is_i915_gem(fd);
23903b705cfSriastradh	if (ret && !hosted()) {
24003b705cfSriastradh		struct drm_mode_card_res res;
24103b705cfSriastradh
24203b705cfSriastradh		memset(&res, 0, sizeof(res));
24303b705cfSriastradh		if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
24442542f5fSchristos			ret = 0;
24503b705cfSriastradh	}
24603b705cfSriastradh
24703b705cfSriastradh	return ret;
24803b705cfSriastradh}
24903b705cfSriastradh
25042542f5fSchristosstatic int open_cloexec(const char *path)
25103b705cfSriastradh{
25242542f5fSchristos	struct stat st;
25342542f5fSchristos	int loop = 1000;
25442542f5fSchristos	int fd;
25503b705cfSriastradh
25642542f5fSchristos	/* No file? Assume the driver is loading slowly */
25742542f5fSchristos	while (stat(path, &st) == -1 && errno == ENOENT && --loop)
25842542f5fSchristos		usleep(50000);
25942542f5fSchristos
26042542f5fSchristos	if (loop != 1000)
26142542f5fSchristos		ErrorF("intel: waited %d ms for '%s' to appear\n",
26242542f5fSchristos		       (1000 - loop) * 50, path);
26342542f5fSchristos
26442542f5fSchristos	fd = -1;
26542542f5fSchristos#ifdef O_CLOEXEC
26642542f5fSchristos	fd = open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC);
26742542f5fSchristos#endif
26803b705cfSriastradh	if (fd == -1)
26942542f5fSchristos		fd = fd_set_cloexec(open(path, O_RDWR | O_NONBLOCK));
27003b705cfSriastradh
27142542f5fSchristos	return fd;
27242542f5fSchristos}
27342542f5fSchristos
27442542f5fSchristos#ifdef __linux__
27542542f5fSchristosstatic int __intel_open_device__major_minor(int _major, int _minor)
27642542f5fSchristos{
27742542f5fSchristos	char path[256];
27842542f5fSchristos	DIR *dir;
27942542f5fSchristos	struct dirent *de;
28042542f5fSchristos	int base, fd = -1;
28142542f5fSchristos
28242542f5fSchristos	base = sprintf(path, "/dev/dri/");
28342542f5fSchristos
28442542f5fSchristos	dir = opendir(path);
28542542f5fSchristos	if (dir == NULL)
28642542f5fSchristos		return -1;
28742542f5fSchristos
28842542f5fSchristos	while ((de = readdir(dir)) != NULL) {
28942542f5fSchristos		struct stat st;
29042542f5fSchristos
29142542f5fSchristos		if (*de->d_name == '.')
29242542f5fSchristos			continue;
29342542f5fSchristos
29442542f5fSchristos		sprintf(path + base, "%s", de->d_name);
29542542f5fSchristos		if (stat(path, &st) == 0 &&
29642542f5fSchristos		    major(st.st_rdev) == _major &&
29742542f5fSchristos		    minor(st.st_rdev) == _minor) {
29842542f5fSchristos			fd = open_cloexec(path);
29942542f5fSchristos			break;
30042542f5fSchristos		}
30103b705cfSriastradh	}
30242542f5fSchristos
30342542f5fSchristos	closedir(dir);
30403b705cfSriastradh
30503b705cfSriastradh	return fd;
30603b705cfSriastradh}
30703b705cfSriastradh
30842542f5fSchristosstatic int __intel_open_device__pci(const struct pci_device *pci)
30903b705cfSriastradh{
31042542f5fSchristos	struct stat st;
31142542f5fSchristos	char path[256];
31242542f5fSchristos	DIR *dir;
31342542f5fSchristos	struct dirent *de;
31442542f5fSchristos	int base;
31503b705cfSriastradh	int fd;
31603b705cfSriastradh
31742542f5fSchristos	/* Look up the major:minor for the drm device through sysfs.
31842542f5fSchristos	 * First we need to check that sysfs is available, then
31942542f5fSchristos	 * check that we have loaded our driver. When we are happy
32042542f5fSchristos	 * that our KMS module is loaded, we can then search for our
32142542f5fSchristos	 * device node. We make the assumption that it uses the same
32242542f5fSchristos	 * name, but after that we read the major:minor assigned to us
32342542f5fSchristos	 * and search for a matching entry in /dev.
32442542f5fSchristos	 */
32542542f5fSchristos
32642542f5fSchristos	base = sprintf(path,
32742542f5fSchristos		       "/sys/bus/pci/devices/%04x:%02x:%02x.%d/",
32842542f5fSchristos		       pci->domain, pci->bus, pci->dev, pci->func);
32942542f5fSchristos	if (stat(path, &st))
33042542f5fSchristos		return -1;
33103b705cfSriastradh
33242542f5fSchristos	sprintf(path + base, "drm");
33342542f5fSchristos	dir = opendir(path);
33442542f5fSchristos	if (dir == NULL) {
33542542f5fSchristos		int loop = 0;
33603b705cfSriastradh
33742542f5fSchristos		sprintf(path + base, "driver");
33842542f5fSchristos		if (stat(path, &st)) {
33903b705cfSriastradh			if (xf86LoadKernelModule("i915"))
34003b705cfSriastradh				return -1;
34103b705cfSriastradh			(void)xf86LoadKernelModule("fbcon");
34203b705cfSriastradh		}
34303b705cfSriastradh
34442542f5fSchristos		sprintf(path + base, "drm");
34542542f5fSchristos		while ((dir = opendir(path)) == NULL && loop++ < 100)
34642542f5fSchristos			usleep(20000);
34742542f5fSchristos
34842542f5fSchristos		ErrorF("intel: waited %d ms for i915.ko driver to load\n", loop * 20000 / 1000);
34942542f5fSchristos
35042542f5fSchristos		if (dir == NULL)
35142542f5fSchristos			return -1;
35242542f5fSchristos	}
35342542f5fSchristos
35442542f5fSchristos	fd = -1;
35542542f5fSchristos	while ((de = readdir(dir)) != NULL) {
35642542f5fSchristos		if (*de->d_name == '.')
35742542f5fSchristos			continue;
35842542f5fSchristos
35942542f5fSchristos		if (strncmp(de->d_name, "card", 4) == 0) {
36042542f5fSchristos			sprintf(path + base + 4, "/dev/dri/%s", de->d_name);
36142542f5fSchristos			fd = open_cloexec(path + base + 4);
36242542f5fSchristos			if (fd != -1)
36342542f5fSchristos				break;
36442542f5fSchristos
36542542f5fSchristos			sprintf(path + base + 3, "/%s/dev", de->d_name);
36642542f5fSchristos			fd = open(path, O_RDONLY);
36742542f5fSchristos			if (fd == -1)
36842542f5fSchristos				break;
36942542f5fSchristos
37042542f5fSchristos			base = read(fd, path, sizeof(path) - 1);
37142542f5fSchristos			close(fd);
37242542f5fSchristos
37342542f5fSchristos			fd = -1;
37442542f5fSchristos			if (base > 0) {
37542542f5fSchristos				int major, minor;
37642542f5fSchristos				path[base] = '\0';
37742542f5fSchristos				if (sscanf(path, "%d:%d", &major, &minor) == 2)
37842542f5fSchristos					fd = __intel_open_device__major_minor(major, minor);
37903b705cfSriastradh			}
38042542f5fSchristos			break;
38103b705cfSriastradh		}
38242542f5fSchristos	}
38342542f5fSchristos	closedir(dir);
38442542f5fSchristos
38542542f5fSchristos	return fd;
38642542f5fSchristos}
38703b705cfSriastradh#else
38842542f5fSchristosstatic int __intel_open_device__pci(const struct pci_device *pci) { return -1; }
38903b705cfSriastradh#endif
39042542f5fSchristos
39142542f5fSchristosstatic int __intel_open_device__legacy(const struct pci_device *pci)
39242542f5fSchristos{
39342542f5fSchristos	char id[20];
39442542f5fSchristos	int ret;
39542542f5fSchristos
39642542f5fSchristos	snprintf(id, sizeof(id),
39742542f5fSchristos		 "pci:%04x:%02x:%02x.%d",
39842542f5fSchristos		 pci->domain, pci->bus, pci->dev, pci->func);
39942542f5fSchristos
40042542f5fSchristos	ret = drmCheckModesettingSupported(id);
40142542f5fSchristos	if (ret) {
40242542f5fSchristos		if (xf86LoadKernelModule("i915"))
40342542f5fSchristos			ret = drmCheckModesettingSupported(id);
40442542f5fSchristos		if (ret)
40542542f5fSchristos			return -1;
40642542f5fSchristos		/* Be nice to the user and load fbcon too */
40742542f5fSchristos		(void)xf86LoadKernelModule("fbcon");
40803b705cfSriastradh	}
40903b705cfSriastradh
41042542f5fSchristos	return fd_set_nonblock(drmOpen(NULL, id));
41142542f5fSchristos}
41242542f5fSchristos
41342542f5fSchristosstatic int __intel_open_device(const struct pci_device *pci, const char *path)
41442542f5fSchristos{
41542542f5fSchristos	int fd;
41642542f5fSchristos
41742542f5fSchristos	if (path == NULL) {
41842542f5fSchristos		if (pci == NULL)
41942542f5fSchristos			return -1;
42042542f5fSchristos
42142542f5fSchristos		fd = __intel_open_device__pci(pci);
42242542f5fSchristos		if (fd == -1)
42342542f5fSchristos			fd = __intel_open_device__legacy(pci);
42442542f5fSchristos	} else
42542542f5fSchristos		fd = open_cloexec(path);
42642542f5fSchristos
42703b705cfSriastradh	return fd;
42803b705cfSriastradh}
42903b705cfSriastradh
43042542f5fSchristosstatic char *find_master_node(int fd)
43142542f5fSchristos{
43242542f5fSchristos	struct stat st, master;
43342542f5fSchristos	char buf[128];
43442542f5fSchristos
43542542f5fSchristos	if (fstat(fd, &st))
43642542f5fSchristos		return NULL;
43742542f5fSchristos
43842542f5fSchristos	if (!S_ISCHR(st.st_mode))
43942542f5fSchristos		return NULL;
44042542f5fSchristos
44142542f5fSchristos	sprintf(buf, "/dev/dri/card%d", (int)(st.st_rdev & 0x7f));
44242542f5fSchristos	if (stat(buf, &master) == 0 &&
44342542f5fSchristos	    st.st_mode == master.st_mode &&
44442542f5fSchristos	    (st.st_rdev & 0x7f) == master.st_rdev)
44542542f5fSchristos		return strdup(buf);
44642542f5fSchristos
44742542f5fSchristos	/* Fallback to iterating over the usual suspects */
44842542f5fSchristos	return drmGetDeviceNameFromFd(fd);
44942542f5fSchristos}
45042542f5fSchristos
45142542f5fSchristosstatic int is_render_node(int fd, struct stat *st)
45242542f5fSchristos{
45342542f5fSchristos	if (fstat(fd, st))
45442542f5fSchristos		return 0;
45542542f5fSchristos
45642542f5fSchristos	if (!S_ISCHR(st->st_mode))
45742542f5fSchristos		return 0;
45842542f5fSchristos
45942542f5fSchristos	return st->st_rdev & 0x80;
46042542f5fSchristos}
46142542f5fSchristos
46242542f5fSchristosstatic char *find_render_node(int fd)
46342542f5fSchristos{
46442542f5fSchristos#if defined(USE_RENDERNODE)
46542542f5fSchristos	struct stat master, render;
46642542f5fSchristos	char buf[128];
46742542f5fSchristos
46842542f5fSchristos	/* Are we a render-node ourselves? */
46942542f5fSchristos	if (is_render_node(fd, &master))
47042542f5fSchristos		return NULL;
47142542f5fSchristos
47242542f5fSchristos	sprintf(buf, "/dev/dri/renderD%d", (int)((master.st_rdev | 0x80) & 0xbf));
47342542f5fSchristos	if (stat(buf, &render) == 0 &&
47442542f5fSchristos	    master.st_mode == render.st_mode &&
47542542f5fSchristos	    render.st_rdev == ((master.st_rdev | 0x80) & 0xbf))
47642542f5fSchristos		return strdup(buf);
47742542f5fSchristos#endif
47842542f5fSchristos
47942542f5fSchristos	return NULL;
48042542f5fSchristos}
48142542f5fSchristos
48242542f5fSchristos#if defined(ODEV_ATTRIB_PATH)
48342542f5fSchristosstatic char *get_path(struct xf86_platform_device *dev)
48442542f5fSchristos{
48542542f5fSchristos	const char *path;
48642542f5fSchristos
48742542f5fSchristos	if (dev == NULL)
48842542f5fSchristos		return NULL;
48942542f5fSchristos
49042542f5fSchristos	path = xf86_get_platform_device_attrib(dev, ODEV_ATTRIB_PATH);
49142542f5fSchristos	if (path == NULL)
49242542f5fSchristos		return NULL;
49342542f5fSchristos
49442542f5fSchristos	return strdup(path);
49542542f5fSchristos}
49642542f5fSchristos
49742542f5fSchristos#else
49842542f5fSchristos
49942542f5fSchristosstatic char *get_path(struct xf86_platform_device *dev)
50042542f5fSchristos{
50142542f5fSchristos	return NULL;
50242542f5fSchristos}
50342542f5fSchristos#endif
50442542f5fSchristos
50542542f5fSchristos
50642542f5fSchristos#if defined(ODEV_ATTRIB_FD)
50742542f5fSchristosstatic int get_fd(struct xf86_platform_device *dev)
50842542f5fSchristos{
50942542f5fSchristos	if (dev == NULL)
51042542f5fSchristos		return -1;
51142542f5fSchristos
51242542f5fSchristos	return xf86_get_platform_device_int_attrib(dev, ODEV_ATTRIB_FD, -1);
51342542f5fSchristos}
51442542f5fSchristos
51542542f5fSchristos#else
51642542f5fSchristos
51742542f5fSchristosstatic int get_fd(struct xf86_platform_device *dev)
51842542f5fSchristos{
51942542f5fSchristos	return -1;
52042542f5fSchristos}
52142542f5fSchristos#endif
52242542f5fSchristos
52342542f5fSchristosstatic int is_master(int fd)
52442542f5fSchristos{
52542542f5fSchristos	drmSetVersion sv;
52642542f5fSchristos
52742542f5fSchristos	sv.drm_di_major = 1;
52842542f5fSchristos	sv.drm_di_minor = 1;
52942542f5fSchristos	sv.drm_dd_major = -1;
53042542f5fSchristos	sv.drm_dd_minor = -1;
53142542f5fSchristos
53242542f5fSchristos	return drmIoctl(fd, DRM_IOCTL_SET_VERSION, &sv) == 0;
53342542f5fSchristos}
53442542f5fSchristos
53503b705cfSriastradhint intel_open_device(int entity_num,
53603b705cfSriastradh		      const struct pci_device *pci,
53742542f5fSchristos		      struct xf86_platform_device *platform)
53803b705cfSriastradh{
53903b705cfSriastradh	struct intel_device *dev;
54042542f5fSchristos	char *path;
54142542f5fSchristos	int fd, master_count;
54203b705cfSriastradh
54303b705cfSriastradh	if (intel_device_key == -1)
54403b705cfSriastradh		intel_device_key = xf86AllocateEntityPrivateIndex();
54503b705cfSriastradh	if (intel_device_key == -1)
54603b705cfSriastradh		return -1;
54703b705cfSriastradh
54803b705cfSriastradh	dev = xf86GetEntityPrivate(entity_num, intel_device_key)->ptr;
54903b705cfSriastradh	if (dev)
55003b705cfSriastradh		return dev->fd;
55103b705cfSriastradh
55242542f5fSchristos	path = get_path(platform);
55303b705cfSriastradh
55442542f5fSchristos	master_count = 1; /* DRM_MASTER is managed by Xserver */
55542542f5fSchristos	fd = get_fd(platform);
55642542f5fSchristos	if (fd == -1) {
55742542f5fSchristos		fd = __intel_open_device(pci, path);
55842542f5fSchristos		if (fd == -1)
55942542f5fSchristos			goto err_path;
56042542f5fSchristos
56142542f5fSchristos		master_count = 0;
56242542f5fSchristos	}
56342542f5fSchristos
56442542f5fSchristos	if (path == NULL) {
56542542f5fSchristos		path = find_master_node(fd);
56642542f5fSchristos		if (path == NULL)
56742542f5fSchristos			goto err_close;
56842542f5fSchristos	}
56903b705cfSriastradh
57003b705cfSriastradh	if (!__intel_check_device(fd))
57103b705cfSriastradh		goto err_close;
57203b705cfSriastradh
57303b705cfSriastradh	dev = malloc(sizeof(*dev));
57403b705cfSriastradh	if (dev == NULL)
57503b705cfSriastradh		goto err_close;
57603b705cfSriastradh
57703b705cfSriastradh	/* If hosted under a system compositor, just pretend to be master */
57842542f5fSchristos	if (hosted())
57942542f5fSchristos		master_count++;
58042542f5fSchristos
58142542f5fSchristos	/* Non-root user holding MASTER, don't let go */
58242542f5fSchristos	if (geteuid() && is_master(fd))
58342542f5fSchristos		master_count++;
58442542f5fSchristos
58513496ba1Ssnj	if (pci)
58613496ba1Ssnj		dev->device_id = pci->device_id;
58713496ba1Ssnj	else
58813496ba1Ssnj		dev->device_id = __intel_get_device_id(fd);
58913496ba1Ssnj
59013496ba1Ssnj	dev->idx = entity_num;
59142542f5fSchristos	dev->fd = fd;
59242542f5fSchristos	dev->open_count = master_count;
59342542f5fSchristos	dev->master_count = master_count;
59442542f5fSchristos	dev->master_node = path;
59542542f5fSchristos	dev->render_node = find_render_node(fd);
59642542f5fSchristos	if (dev->render_node == NULL)
59742542f5fSchristos		dev->render_node = dev->master_node;
59803b705cfSriastradh
59903b705cfSriastradh	xf86GetEntityPrivate(entity_num, intel_device_key)->ptr = dev;
60003b705cfSriastradh
60103b705cfSriastradh	return fd;
60203b705cfSriastradh
60303b705cfSriastradherr_close:
60442542f5fSchristos	if (master_count == 0) /* Don't close server-fds */
60542542f5fSchristos		close(fd);
60603b705cfSriastradherr_path:
60742542f5fSchristos	free(path);
60803b705cfSriastradh	return -1;
60903b705cfSriastradh}
61003b705cfSriastradh
61142542f5fSchristosint __intel_peek_fd(ScrnInfoPtr scrn)
61242542f5fSchristos{
61342542f5fSchristos	struct intel_device *dev;
61442542f5fSchristos
61542542f5fSchristos	dev = intel_device(scrn);
61642542f5fSchristos	assert(dev && dev->fd != -1);
61742542f5fSchristos
61842542f5fSchristos	return dev->fd;
61942542f5fSchristos}
62042542f5fSchristos
62113496ba1Ssnjint intel_has_render_node(struct intel_device *dev)
62242542f5fSchristos{
62342542f5fSchristos	struct stat st;
62442542f5fSchristos
62542542f5fSchristos	assert(dev && dev->fd != -1);
62642542f5fSchristos	return is_render_node(dev->fd, &st);
62742542f5fSchristos}
62842542f5fSchristos
62913496ba1Ssnjstruct intel_device *intel_get_device(ScrnInfoPtr scrn, int *fd)
63003b705cfSriastradh{
63103b705cfSriastradh	struct intel_device *dev;
63203b705cfSriastradh	int ret;
63303b705cfSriastradh
63403b705cfSriastradh	dev = intel_device(scrn);
63513496ba1Ssnj	if (dev == NULL)
63613496ba1Ssnj		return NULL;
63713496ba1Ssnj
63813496ba1Ssnj	assert(dev->fd != -1);
63903b705cfSriastradh
64003b705cfSriastradh	if (dev->open_count++ == 0) {
64103b705cfSriastradh		drmSetVersion sv;
64203b705cfSriastradh		int retry = 2000;
64303b705cfSriastradh
64403b705cfSriastradh		assert(!hosted());
64503b705cfSriastradh
64603b705cfSriastradh		/* Check that what we opened was a master or a
64703b705cfSriastradh		 * master-capable FD, by setting the version of the
64803b705cfSriastradh		 * interface we'll use to talk to it.
64903b705cfSriastradh		 */
65003b705cfSriastradh		do {
65103b705cfSriastradh			sv.drm_di_major = 1;
65203b705cfSriastradh			sv.drm_di_minor = 1;
65303b705cfSriastradh			sv.drm_dd_major = -1;
65403b705cfSriastradh			sv.drm_dd_minor = -1;
65503b705cfSriastradh			ret = drmIoctl(dev->fd, DRM_IOCTL_SET_VERSION, &sv);
65603b705cfSriastradh			if (ret == 0)
65703b705cfSriastradh				break;
65803b705cfSriastradh
65903b705cfSriastradh			usleep(1000);
66003b705cfSriastradh		} while (--retry);
66103b705cfSriastradh		if (ret != 0) {
66203b705cfSriastradh			xf86DrvMsg(scrn->scrnIndex, X_ERROR,
66303b705cfSriastradh				   "[drm] failed to set drm interface version: %s [%d].\n",
66403b705cfSriastradh				   strerror(errno), errno);
66542542f5fSchristos			dump_clients_info(scrn, dev->fd);
66603b705cfSriastradh			dev->open_count--;
66713496ba1Ssnj			return NULL;
66803b705cfSriastradh		}
66903b705cfSriastradh	}
67003b705cfSriastradh
67113496ba1Ssnj	*fd = dev->fd;
67213496ba1Ssnj	return dev;
67303b705cfSriastradh}
67403b705cfSriastradh
67513496ba1Ssnjconst char *intel_get_client_name(struct intel_device *dev)
67642542f5fSchristos{
67742542f5fSchristos	assert(dev && dev->render_node);
67842542f5fSchristos	return dev->render_node;
67942542f5fSchristos}
68042542f5fSchristos
68142542f5fSchristosstatic int authorise(struct intel_device *dev, int fd)
68242542f5fSchristos{
68342542f5fSchristos	struct stat st;
68442542f5fSchristos	drm_magic_t magic;
68542542f5fSchristos
68642542f5fSchristos	if (is_render_node(fd, &st)) /* restricted authority, do not elevate */
68742542f5fSchristos		return 1;
68842542f5fSchristos
68942542f5fSchristos	return drmGetMagic(fd, &magic) == 0 && drmAuthMagic(dev->fd, magic) == 0;
69042542f5fSchristos}
69142542f5fSchristos
69213496ba1Ssnjint intel_get_client_fd(struct intel_device *dev)
69342542f5fSchristos{
69442542f5fSchristos	int fd = -1;
69542542f5fSchristos
69613496ba1Ssnj	assert(dev && 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
71713496ba1Ssnjint intel_get_device_id(struct intel_device *dev)
71803b705cfSriastradh{
71942542f5fSchristos	assert(dev && dev->fd != -1);
72013496ba1Ssnj	return dev->device_id;
72103b705cfSriastradh}
72203b705cfSriastradh
72313496ba1Ssnjint intel_get_master(struct intel_device *dev)
72403b705cfSriastradh{
72503b705cfSriastradh	int ret;
72603b705cfSriastradh
72703b705cfSriastradh	assert(dev && dev->fd != -1);
72803b705cfSriastradh
72903b705cfSriastradh	ret = 0;
73003b705cfSriastradh	if (dev->master_count++ == 0) {
73103b705cfSriastradh		int retry = 2000;
73203b705cfSriastradh
73303b705cfSriastradh		assert(!hosted());
73403b705cfSriastradh		do {
73503b705cfSriastradh			ret = drmSetMaster(dev->fd);
73603b705cfSriastradh			if (ret == 0)
73703b705cfSriastradh				break;
73803b705cfSriastradh			usleep(1000);
73903b705cfSriastradh		} while (--retry);
74003b705cfSriastradh	}
74103b705cfSriastradh
74203b705cfSriastradh	return ret;
74303b705cfSriastradh}
74403b705cfSriastradh
74513496ba1Ssnjint intel_put_master(struct intel_device *dev)
74603b705cfSriastradh{
74703b705cfSriastradh	int ret;
74803b705cfSriastradh
74903b705cfSriastradh	assert(dev && dev->fd != -1);
75003b705cfSriastradh
75103b705cfSriastradh	ret = 0;
75203b705cfSriastradh	assert(dev->master_count);
75303b705cfSriastradh	if (--dev->master_count == 0) {
75403b705cfSriastradh		assert(!hosted());
75503b705cfSriastradh		assert(drmSetMaster(dev->fd) == 0);
75603b705cfSriastradh		ret = drmDropMaster(dev->fd);
75703b705cfSriastradh	}
75803b705cfSriastradh
75903b705cfSriastradh	return ret;
76003b705cfSriastradh}
76103b705cfSriastradh
76213496ba1Ssnjvoid intel_put_device(struct intel_device *dev)
76303b705cfSriastradh{
76403b705cfSriastradh	assert(dev && dev->fd != -1);
76503b705cfSriastradh
76603b705cfSriastradh	assert(dev->open_count);
76703b705cfSriastradh	if (--dev->open_count)
76803b705cfSriastradh		return;
76903b705cfSriastradh
77003b705cfSriastradh	assert(!hosted());
77113496ba1Ssnj	xf86GetEntityPrivate(dev->idx, intel_device_key)->ptr = NULL;
77203b705cfSriastradh
77303b705cfSriastradh	drmClose(dev->fd);
77442542f5fSchristos	if (dev->render_node != dev->master_node)
77542542f5fSchristos		free(dev->render_node);
77642542f5fSchristos	free(dev->master_node);
77703b705cfSriastradh	free(dev);
77803b705cfSriastradh}
779