drm_ioctl.c 9.7 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 36 37 38 39 40 41 42
 * 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.
 */

#include "drmP.h"
#include "drm_core.h"

#include "linux/pci.h"

/**
 * Get the bus id.
D
Dave Airlie 已提交
43
 *
L
Linus Torvalds 已提交
44 45 46 47 48 49 50 51 52
 * \param inode device inode.
 * \param filp file pointer.
 * \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.
 */
int drm_getunique(struct inode *inode, struct file *filp,
D
Dave Airlie 已提交
53
		  unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
54
{
D
Dave Airlie 已提交
55 56 57 58
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->head->dev;
	drm_unique_t __user *argp = (void __user *)arg;
	drm_unique_t u;
L
Linus Torvalds 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73

	if (copy_from_user(&u, argp, sizeof(u)))
		return -EFAULT;
	if (u.unique_len >= dev->unique_len) {
		if (copy_to_user(u.unique, dev->unique, dev->unique_len))
			return -EFAULT;
	}
	u.unique_len = dev->unique_len;
	if (copy_to_user(argp, &u, sizeof(u)))
		return -EFAULT;
	return 0;
}

/**
 * Set the bus id.
D
Dave Airlie 已提交
74
 *
L
Linus Torvalds 已提交
75 76 77 78 79 80 81 82 83 84 85 86
 * \param inode device inode.
 * \param filp file pointer.
 * \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.
 */
int drm_setunique(struct inode *inode, struct file *filp,
D
Dave Airlie 已提交
87
		  unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
88
{
D
Dave Airlie 已提交
89 90 91 92
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->head->dev;
	drm_unique_t u;
	int domain, bus, slot, func, ret;
L
Linus Torvalds 已提交
93

D
Dave Airlie 已提交
94 95
	if (dev->unique_len || dev->unique)
		return -EBUSY;
L
Linus Torvalds 已提交
96

D
Dave Airlie 已提交
97
	if (copy_from_user(&u, (drm_unique_t __user *) arg, sizeof(u)))
L
Linus Torvalds 已提交
98 99
		return -EFAULT;

D
Dave Airlie 已提交
100 101
	if (!u.unique_len || u.unique_len > 1024)
		return -EINVAL;
L
Linus Torvalds 已提交
102 103

	dev->unique_len = u.unique_len;
D
Dave Airlie 已提交
104 105 106
	dev->unique = drm_alloc(u.unique_len + 1, DRM_MEM_DRIVER);
	if (!dev->unique)
		return -ENOMEM;
L
Linus Torvalds 已提交
107 108 109 110 111
	if (copy_from_user(dev->unique, u.unique, dev->unique_len))
		return -EFAULT;

	dev->unique[dev->unique_len] = '\0';

D
Dave Airlie 已提交
112 113 114
	dev->devname =
	    drm_alloc(strlen(dev->driver->pci_driver.name) +
		      strlen(dev->unique) + 2, DRM_MEM_DRIVER);
L
Linus Torvalds 已提交
115 116 117
	if (!dev->devname)
		return -ENOMEM;

D
Dave Airlie 已提交
118 119
	sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
		dev->unique);
L
Linus Torvalds 已提交
120 121 122 123 124 125 126 127 128

	/* Return error if the busid submitted doesn't match the device's actual
	 * busid.
	 */
	ret = sscanf(dev->unique, "PCI:%d:%d:%d", &bus, &slot, &func);
	if (ret != 3)
		return DRM_ERR(EINVAL);
	domain = bus >> 8;
	bus &= 0xff;
D
Dave Airlie 已提交
129

L
Linus Torvalds 已提交
130 131
	if ((domain != dev->pci_domain) ||
	    (bus != dev->pci_bus) ||
D
Dave Airlie 已提交
132
	    (slot != dev->pci_slot) || (func != dev->pci_func))
L
Linus Torvalds 已提交
133 134 135 136 137
		return -EINVAL;

	return 0;
}

D
Dave Airlie 已提交
138
static int drm_set_busid(drm_device_t * dev)
L
Linus Torvalds 已提交
139
{
140 141
	int len;

L
Linus Torvalds 已提交
142 143 144
	if (dev->unique != NULL)
		return EBUSY;

145
	dev->unique_len = 40;
L
Linus Torvalds 已提交
146 147 148 149
	dev->unique = drm_alloc(dev->unique_len + 1, DRM_MEM_DRIVER);
	if (dev->unique == NULL)
		return ENOMEM;

150
	len = snprintf(dev->unique, dev->unique_len, "pci:%04x:%02x:%02x.%d",
D
Dave Airlie 已提交
151
		 dev->pci_domain, dev->pci_bus, dev->pci_slot, dev->pci_func);
L
Linus Torvalds 已提交
152

153 154 155
	if (len > dev->unique_len)
		DRM_ERROR("Unique buffer overflowed\n");

D
Dave Airlie 已提交
156 157 158
	dev->devname =
	    drm_alloc(strlen(dev->driver->pci_driver.name) + dev->unique_len +
		      2, DRM_MEM_DRIVER);
L
Linus Torvalds 已提交
159 160 161
	if (dev->devname == NULL)
		return ENOMEM;

D
Dave Airlie 已提交
162 163
	sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
		dev->unique);
L
Linus Torvalds 已提交
164 165 166 167 168 169 170 171 172 173 174

	return 0;
}

/**
 * Get a mapping information.
 *
 * \param inode device inode.
 * \param filp file pointer.
 * \param cmd command.
 * \param arg user argument, pointing to a drm_map structure.
D
Dave Airlie 已提交
175
 *
L
Linus Torvalds 已提交
176 177 178 179 180
 * \return zero on success or a negative number on failure.
 *
 * Searches for the mapping with the specified offset and copies its information
 * into userspace
 */
D
Dave Airlie 已提交
181 182
int drm_getmap(struct inode *inode, struct file *filp,
	       unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
183
{
D
Dave Airlie 已提交
184 185 186 187
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->head->dev;
	drm_map_t __user *argp = (void __user *)arg;
	drm_map_t map;
L
Linus Torvalds 已提交
188 189
	drm_map_list_t *r_list = NULL;
	struct list_head *list;
D
Dave Airlie 已提交
190 191
	int idx;
	int i;
L
Linus Torvalds 已提交
192 193 194 195 196

	if (copy_from_user(&map, argp, sizeof(map)))
		return -EFAULT;
	idx = map.offset;

D
Dave Airlie 已提交
197
	mutex_lock(&dev->struct_mutex);
L
Linus Torvalds 已提交
198
	if (idx < 0) {
D
Dave Airlie 已提交
199
		mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
200 201 202 203 204
		return -EINVAL;
	}

	i = 0;
	list_for_each(list, &dev->maplist->head) {
D
Dave Airlie 已提交
205
		if (i == idx) {
L
Linus Torvalds 已提交
206 207 208 209 210
			r_list = list_entry(list, drm_map_list_t, head);
			break;
		}
		i++;
	}
D
Dave Airlie 已提交
211
	if (!r_list || !r_list->map) {
D
Dave Airlie 已提交
212
		mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
213 214 215 216
		return -EINVAL;
	}

	map.offset = r_list->map->offset;
D
Dave Airlie 已提交
217 218 219 220 221
	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;
	map.mtrr = r_list->map->mtrr;
D
Dave Airlie 已提交
222
	mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
223

D
Dave Airlie 已提交
224 225
	if (copy_to_user(argp, &map, sizeof(map)))
		return -EFAULT;
L
Linus Torvalds 已提交
226 227 228 229 230 231 232 233 234 235
	return 0;
}

/**
 * Get client information.
 *
 * \param inode device inode.
 * \param filp file pointer.
 * \param cmd command.
 * \param arg user argument, pointing to a drm_client structure.
D
Dave Airlie 已提交
236
 *
L
Linus Torvalds 已提交
237 238 239 240 241
 * \return zero on success or a negative number on failure.
 *
 * Searches for the client with the specified index and copies its information
 * into userspace
 */
D
Dave Airlie 已提交
242 243
int drm_getclient(struct inode *inode, struct file *filp,
		  unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
244
{
D
Dave Airlie 已提交
245 246
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->head->dev;
247
	drm_client_t __user *argp = (drm_client_t __user *)arg;
L
Linus Torvalds 已提交
248
	drm_client_t client;
D
Dave Airlie 已提交
249 250 251
	drm_file_t *pt;
	int idx;
	int i;
L
Linus Torvalds 已提交
252 253 254 255

	if (copy_from_user(&client, argp, sizeof(client)))
		return -EFAULT;
	idx = client.idx;
D
Dave Airlie 已提交
256
	mutex_lock(&dev->struct_mutex);
D
Dave Airlie 已提交
257
	for (i = 0, pt = dev->file_first; i < idx && pt; i++, pt = pt->next) ;
L
Linus Torvalds 已提交
258 259

	if (!pt) {
D
Dave Airlie 已提交
260
		mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
261 262
		return -EINVAL;
	}
D
Dave Airlie 已提交
263 264 265
	client.auth = pt->authenticated;
	client.pid = pt->pid;
	client.uid = pt->uid;
L
Linus Torvalds 已提交
266
	client.magic = pt->magic;
D
Dave Airlie 已提交
267
	client.iocs = pt->ioctl_count;
D
Dave Airlie 已提交
268
	mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
269

270
	if (copy_to_user(argp, &client, sizeof(client)))
L
Linus Torvalds 已提交
271 272 273 274
		return -EFAULT;
	return 0;
}

D
Dave Airlie 已提交
275 276 277
/**
 * Get statistics information.
 *
L
Linus Torvalds 已提交
278 279 280 281
 * \param inode device inode.
 * \param filp file pointer.
 * \param cmd command.
 * \param arg user argument, pointing to a drm_stats structure.
D
Dave Airlie 已提交
282
 *
L
Linus Torvalds 已提交
283 284
 * \return zero on success or a negative number on failure.
 */
D
Dave Airlie 已提交
285 286
int drm_getstats(struct inode *inode, struct file *filp,
		 unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
287
{
D
Dave Airlie 已提交
288 289 290 291
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->head->dev;
	drm_stats_t stats;
	int i;
L
Linus Torvalds 已提交
292 293

	memset(&stats, 0, sizeof(stats));
D
Dave Airlie 已提交
294

D
Dave Airlie 已提交
295
	mutex_lock(&dev->struct_mutex);
L
Linus Torvalds 已提交
296 297 298 299

	for (i = 0; i < dev->counters; i++) {
		if (dev->types[i] == _DRM_STAT_LOCK)
			stats.data[i].value
D
Dave Airlie 已提交
300 301
			    = (dev->lock.hw_lock ? dev->lock.hw_lock->lock : 0);
		else
L
Linus Torvalds 已提交
302
			stats.data[i].value = atomic_read(&dev->counts[i]);
D
Dave Airlie 已提交
303
		stats.data[i].type = dev->types[i];
L
Linus Torvalds 已提交
304
	}
D
Dave Airlie 已提交
305

L
Linus Torvalds 已提交
306 307
	stats.count = dev->counters;

D
Dave Airlie 已提交
308
	mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
309

D
Dave Airlie 已提交
310
	if (copy_to_user((drm_stats_t __user *) arg, &stats, sizeof(stats)))
L
Linus Torvalds 已提交
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
		return -EFAULT;
	return 0;
}

/**
 * Setversion ioctl.
 *
 * \param inode device inode.
 * \param filp file pointer.
 * \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
 */
int drm_setversion(DRM_IOCTL_ARGS)
{
	DRM_DEVICE;
	drm_set_version_t sv;
	drm_set_version_t retv;
	int if_version;
	drm_set_version_t __user *argp = (void __user *)data;

	DRM_COPY_FROM_USER_IOCTL(sv, argp, sizeof(sv));

	retv.drm_di_major = DRM_IF_MAJOR;
	retv.drm_di_minor = DRM_IF_MINOR;
338 339
	retv.drm_dd_major = dev->driver->major;
	retv.drm_dd_minor = dev->driver->minor;
L
Linus Torvalds 已提交
340 341 342 343 344 345 346

	DRM_COPY_TO_USER_IOCTL(argp, retv, sizeof(sv));

	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)
			return EINVAL;
347
		if_version = DRM_IF_VERSION(sv.drm_di_major, sv.drm_di_minor);
L
Linus Torvalds 已提交
348 349 350 351 352 353 354 355 356 357
		dev->if_version = DRM_MAX(if_version, dev->if_version);
		if (sv.drm_di_minor >= 1) {
			/*
			 * Version 1.1 includes tying of DRM to specific device
			 */
			drm_set_busid(dev);
		}
	}

	if (sv.drm_dd_major != -1) {
358
		if (sv.drm_dd_major != dev->driver->major ||
D
Dave Airlie 已提交
359
		    sv.drm_dd_minor < 0
360
		    || sv.drm_dd_minor > dev->driver->minor)
L
Linus Torvalds 已提交
361 362 363 364 365 366 367 368 369 370
			return EINVAL;

		if (dev->driver->set_version)
			dev->driver->set_version(dev, &sv);
	}
	return 0;
}

/** No-op ioctl. */
int drm_noop(struct inode *inode, struct file *filp, unsigned int cmd,
D
Dave Airlie 已提交
371
	     unsigned long arg)
L
Linus Torvalds 已提交
372 373 374 375
{
	DRM_DEBUG("\n");
	return 0;
}