drm_ioctl.c 9.1 KB
Newer Older
L
Linus Torvalds 已提交
1
/**
D
Dave Airlie 已提交
2
 * \file drm_ioctl.c
L
Linus Torvalds 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
 * IOCTL processing for DRM
 *
 * \author Rickard E. (Rik) Faith <faith@valinux.com>
 * \author Gareth Hughes <gareth@valinux.com>
 */

/*
 * Created: Fri Jan  8 09:01:26 1999 by faith@valinux.com
 *
 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

36 37
#include <drm/drmP.h>
#include <drm/drm_core.h>
L
Linus Torvalds 已提交
38

39 40
#include <linux/pci.h>
#include <linux/export.h>
41 42 43
#ifdef CONFIG_X86
#include <asm/mtrr.h>
#endif
L
Linus Torvalds 已提交
44 45 46

/**
 * Get the bus id.
D
Dave Airlie 已提交
47
 *
L
Linus Torvalds 已提交
48
 * \param inode device inode.
49
 * \param file_priv DRM file private.
L
Linus Torvalds 已提交
50 51 52 53 54 55
 * \param cmd command.
 * \param arg user argument, pointing to a drm_unique structure.
 * \return zero on success or a negative number on failure.
 *
 * Copies the bus id from drm_device::unique into user space.
 */
56 57
int drm_getunique(struct drm_device *dev, void *data,
		  struct drm_file *file_priv)
L
Linus Torvalds 已提交
58
{
59
	struct drm_unique *u = data;
60
	struct drm_master *master = file_priv->master;
L
Linus Torvalds 已提交
61

62 63
	if (u->unique_len >= master->unique_len) {
		if (copy_to_user(u->unique, master->unique, master->unique_len))
L
Linus Torvalds 已提交
64 65
			return -EFAULT;
	}
66
	u->unique_len = master->unique_len;
67

L
Linus Torvalds 已提交
68 69 70
	return 0;
}

71 72 73 74 75 76 77 78 79 80 81 82 83
static void
drm_unset_busid(struct drm_device *dev,
		struct drm_master *master)
{
	kfree(dev->devname);
	dev->devname = NULL;

	kfree(master->unique);
	master->unique = NULL;
	master->unique_len = 0;
	master->unique_size = 0;
}

L
Linus Torvalds 已提交
84 85
/**
 * Set the bus id.
D
Dave Airlie 已提交
86
 *
L
Linus Torvalds 已提交
87
 * \param inode device inode.
88
 * \param file_priv DRM file private.
L
Linus Torvalds 已提交
89 90 91 92 93 94 95 96 97
 * \param cmd command.
 * \param arg user argument, pointing to a drm_unique structure.
 * \return zero on success or a negative number on failure.
 *
 * Copies the bus id from userspace into drm_device::unique, and verifies that
 * it matches the device this DRM is attached to (EINVAL otherwise).  Deprecated
 * in interface version 1.1 and will return EBUSY when setversion has requested
 * version 1.1 or greater.
 */
98 99
int drm_setunique(struct drm_device *dev, void *data,
		  struct drm_file *file_priv)
L
Linus Torvalds 已提交
100
{
101
	struct drm_unique *u = data;
102
	struct drm_master *master = file_priv->master;
103
	int ret;
L
Linus Torvalds 已提交
104

105
	if (master->unique_len || master->unique)
D
Dave Airlie 已提交
106
		return -EBUSY;
L
Linus Torvalds 已提交
107

108
	if (!u->unique_len || u->unique_len > 1024)
D
Dave Airlie 已提交
109
		return -EINVAL;
L
Linus Torvalds 已提交
110

111 112
	if (!dev->driver->bus->set_unique)
		return -EINVAL;
D
Dave Airlie 已提交
113

114 115
	ret = dev->driver->bus->set_unique(dev, master, u);
	if (ret)
116
		goto err;
L
Linus Torvalds 已提交
117 118

	return 0;
119 120 121 122

err:
	drm_unset_busid(dev, master);
	return ret;
L
Linus Torvalds 已提交
123 124
}

125
static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
L
Linus Torvalds 已提交
126
{
127
	struct drm_master *master = file_priv->master;
128
	int ret;
129 130 131

	if (master->unique != NULL)
		drm_unset_busid(dev, master);
132

133 134 135
	ret = dev->driver->bus->set_busid(dev, master);
	if (ret)
		goto err;
L
Linus Torvalds 已提交
136
	return 0;
137 138 139
err:
	drm_unset_busid(dev, master);
	return ret;
L
Linus Torvalds 已提交
140 141 142 143 144 145
}

/**
 * Get a mapping information.
 *
 * \param inode device inode.
146
 * \param file_priv DRM file private.
L
Linus Torvalds 已提交
147 148
 * \param cmd command.
 * \param arg user argument, pointing to a drm_map structure.
D
Dave Airlie 已提交
149
 *
L
Linus Torvalds 已提交
150 151 152 153 154
 * \return zero on success or a negative number on failure.
 *
 * Searches for the mapping with the specified offset and copies its information
 * into userspace
 */
155 156
int drm_getmap(struct drm_device *dev, void *data,
	       struct drm_file *file_priv)
L
Linus Torvalds 已提交
157
{
158
	struct drm_map *map = data;
D
Dave Airlie 已提交
159
	struct drm_map_list *r_list = NULL;
L
Linus Torvalds 已提交
160
	struct list_head *list;
D
Dave Airlie 已提交
161 162
	int idx;
	int i;
L
Linus Torvalds 已提交
163

164
	idx = map->offset;
165
	if (idx < 0)
L
Linus Torvalds 已提交
166 167 168
		return -EINVAL;

	i = 0;
169
	mutex_lock(&dev->struct_mutex);
170
	list_for_each(list, &dev->maplist) {
D
Dave Airlie 已提交
171
		if (i == idx) {
D
Dave Airlie 已提交
172
			r_list = list_entry(list, struct drm_map_list, head);
L
Linus Torvalds 已提交
173 174 175 176
			break;
		}
		i++;
	}
D
Dave Airlie 已提交
177
	if (!r_list || !r_list->map) {
D
Dave Airlie 已提交
178
		mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
179 180 181
		return -EINVAL;
	}

182 183 184 185 186
	map->offset = r_list->map->offset;
	map->size = r_list->map->size;
	map->type = r_list->map->type;
	map->flags = r_list->map->flags;
	map->handle = (void *)(unsigned long) r_list->user_token;
187 188 189 190 191 192 193 194 195 196 197

#ifdef CONFIG_X86
	/*
	 * There appears to be exactly one user of the mtrr index: dritest.
	 * It's easy enough to keep it working on non-PAT systems.
	 */
	map->mtrr = phys_wc_to_mtrr_index(r_list->map->mtrr);
#else
	map->mtrr = -1;
#endif

D
Dave Airlie 已提交
198
	mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
199 200 201 202 203 204 205 206

	return 0;
}

/**
 * Get client information.
 *
 * \param inode device inode.
207
 * \param file_priv DRM file private.
L
Linus Torvalds 已提交
208 209
 * \param cmd command.
 * \param arg user argument, pointing to a drm_client structure.
D
Dave Airlie 已提交
210
 *
L
Linus Torvalds 已提交
211 212 213 214 215
 * \return zero on success or a negative number on failure.
 *
 * Searches for the client with the specified index and copies its information
 * into userspace
 */
216 217
int drm_getclient(struct drm_device *dev, void *data,
		  struct drm_file *file_priv)
L
Linus Torvalds 已提交
218
{
219
	struct drm_client *client = data;
220

D
Daniel Vetter 已提交
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
	/*
	 * Hollowed-out getclient ioctl to keep some dead old drm tests/tools
	 * not breaking completely. Userspace tools stop enumerating one they
	 * get -EINVAL, hence this is the return value we need to hand back for
	 * no clients tracked.
	 *
	 * Unfortunately some clients (*cough* libva *cough*) use this in a fun
	 * attempt to figure out whether they're authenticated or not. Since
	 * that's the only thing they care about, give it to the directly
	 * instead of walking one giant list.
	 */
	if (client->idx == 0) {
		client->auth = file_priv->authenticated;
		client->pid = pid_vnr(file_priv->pid);
		client->uid = from_kuid_munged(current_user_ns(),
					       file_priv->uid);
		client->magic = 0;
		client->iocs = 0;

		return 0;
	} else {
		return -EINVAL;
243
	}
L
Linus Torvalds 已提交
244 245
}

D
Dave Airlie 已提交
246 247 248
/**
 * Get statistics information.
 *
L
Linus Torvalds 已提交
249
 * \param inode device inode.
250
 * \param file_priv DRM file private.
L
Linus Torvalds 已提交
251 252
 * \param cmd command.
 * \param arg user argument, pointing to a drm_stats structure.
D
Dave Airlie 已提交
253
 *
L
Linus Torvalds 已提交
254 255
 * \return zero on success or a negative number on failure.
 */
256 257
int drm_getstats(struct drm_device *dev, void *data,
		 struct drm_file *file_priv)
L
Linus Torvalds 已提交
258
{
259
	struct drm_stats *stats = data;
L
Linus Torvalds 已提交
260

D
Daniel Vetter 已提交
261
	/* Clear stats to prevent userspace from eating its stack garbage. */
L
Li Zefan 已提交
262
	memset(stats, 0, sizeof(*stats));
D
Dave Airlie 已提交
263

L
Linus Torvalds 已提交
264 265 266
	return 0;
}

267 268 269 270 271 272 273 274
/**
 * Get device/driver capabilities
 */
int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
	struct drm_get_cap *req = data;

	req->value = 0;
275 276 277 278 279
	switch (req->capability) {
	case DRM_CAP_DUMB_BUFFER:
		if (dev->driver->dumb_create)
			req->value = 1;
		break;
280
	case DRM_CAP_VBLANK_HIGH_CRTC:
281 282
		req->value = 1;
		break;
283 284 285 286 287 288
	case DRM_CAP_DUMB_PREFERRED_DEPTH:
		req->value = dev->mode_config.preferred_depth;
		break;
	case DRM_CAP_DUMB_PREFER_SHADOW:
		req->value = dev->mode_config.prefer_shadow;
		break;
289 290 291 292
	case DRM_CAP_PRIME:
		req->value |= dev->driver->prime_fd_to_handle ? DRM_PRIME_CAP_IMPORT : 0;
		req->value |= dev->driver->prime_handle_to_fd ? DRM_PRIME_CAP_EXPORT : 0;
		break;
293 294 295
	case DRM_CAP_TIMESTAMP_MONOTONIC:
		req->value = drm_timestamp_monotonic;
		break;
296 297 298
	default:
		return -EINVAL;
	}
299 300 301
	return 0;
}

L
Linus Torvalds 已提交
302 303 304 305
/**
 * Setversion ioctl.
 *
 * \param inode device inode.
306
 * \param file_priv DRM file private.
L
Linus Torvalds 已提交
307 308 309 310 311 312
 * \param cmd command.
 * \param arg user argument, pointing to a drm_lock structure.
 * \return zero on success or negative number on failure.
 *
 * Sets the requested interface version
 */
313
int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv)
L
Linus Torvalds 已提交
314
{
315 316 317 318 319 320 321 322 323 324 325
	struct drm_set_version *sv = data;
	int if_version, retcode = 0;

	if (sv->drm_di_major != -1) {
		if (sv->drm_di_major != DRM_IF_MAJOR ||
		    sv->drm_di_minor < 0 || sv->drm_di_minor > DRM_IF_MINOR) {
			retcode = -EINVAL;
			goto done;
		}
		if_version = DRM_IF_VERSION(sv->drm_di_major,
					    sv->drm_di_minor);
326
		dev->if_version = max(if_version, dev->if_version);
327
		if (sv->drm_di_minor >= 1) {
L
Linus Torvalds 已提交
328 329
			/*
			 * Version 1.1 includes tying of DRM to specific device
330
			 * Version 1.4 has proper PCI domain support
L
Linus Torvalds 已提交
331
			 */
332 333 334
			retcode = drm_set_busid(dev, file_priv);
			if (retcode)
				goto done;
L
Linus Torvalds 已提交
335 336 337
		}
	}

338 339 340 341 342 343 344
	if (sv->drm_dd_major != -1) {
		if (sv->drm_dd_major != dev->driver->major ||
		    sv->drm_dd_minor < 0 || sv->drm_dd_minor >
		    dev->driver->minor) {
			retcode = -EINVAL;
			goto done;
		}
L
Linus Torvalds 已提交
345
	}
346 347 348 349 350 351 352 353

done:
	sv->drm_di_major = DRM_IF_MAJOR;
	sv->drm_di_minor = DRM_IF_MINOR;
	sv->drm_dd_major = dev->driver->major;
	sv->drm_dd_minor = dev->driver->minor;

	return retcode;
L
Linus Torvalds 已提交
354 355 356
}

/** No-op ioctl. */
357 358
int drm_noop(struct drm_device *dev, void *data,
	     struct drm_file *file_priv)
L
Linus Torvalds 已提交
359 360 361 362
{
	DRM_DEBUG("\n");
	return 0;
}
D
Daniel Vetter 已提交
363
EXPORT_SYMBOL(drm_noop);