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 142 143 144 145 146 147 148
{
	if (dev->unique != NULL)
		return EBUSY;

	dev->unique_len = 20;
	dev->unique = drm_alloc(dev->unique_len + 1, DRM_MEM_DRIVER);
	if (dev->unique == NULL)
		return ENOMEM;

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

D
Dave Airlie 已提交
151 152 153
	dev->devname =
	    drm_alloc(strlen(dev->driver->pci_driver.name) + dev->unique_len +
		      2, DRM_MEM_DRIVER);
L
Linus Torvalds 已提交
154 155 156
	if (dev->devname == NULL)
		return ENOMEM;

D
Dave Airlie 已提交
157 158
	sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
		dev->unique);
L
Linus Torvalds 已提交
159 160 161 162 163 164 165 166 167 168 169

	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 已提交
170
 *
L
Linus Torvalds 已提交
171 172 173 174 175
 * \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 已提交
176 177
int drm_getmap(struct inode *inode, struct file *filp,
	       unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
178
{
D
Dave Airlie 已提交
179 180 181 182
	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 已提交
183 184
	drm_map_list_t *r_list = NULL;
	struct list_head *list;
D
Dave Airlie 已提交
185 186
	int idx;
	int i;
L
Linus Torvalds 已提交
187 188 189 190 191 192 193 194 195 196 197 198 199

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

	down(&dev->struct_sem);
	if (idx < 0) {
		up(&dev->struct_sem);
		return -EINVAL;
	}

	i = 0;
	list_for_each(list, &dev->maplist->head) {
D
Dave Airlie 已提交
200
		if (i == idx) {
L
Linus Torvalds 已提交
201 202 203 204 205
			r_list = list_entry(list, drm_map_list_t, head);
			break;
		}
		i++;
	}
D
Dave Airlie 已提交
206
	if (!r_list || !r_list->map) {
L
Linus Torvalds 已提交
207 208 209 210 211
		up(&dev->struct_sem);
		return -EINVAL;
	}

	map.offset = r_list->map->offset;
D
Dave Airlie 已提交
212 213 214 215 216
	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;
L
Linus Torvalds 已提交
217 218
	up(&dev->struct_sem);

D
Dave Airlie 已提交
219 220
	if (copy_to_user(argp, &map, sizeof(map)))
		return -EFAULT;
L
Linus Torvalds 已提交
221 222 223 224 225 226 227 228 229 230
	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 已提交
231
 *
L
Linus Torvalds 已提交
232 233 234 235 236
 * \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 已提交
237 238
int drm_getclient(struct inode *inode, struct file *filp,
		  unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
239
{
D
Dave Airlie 已提交
240 241
	drm_file_t *priv = filp->private_data;
	drm_device_t *dev = priv->head->dev;
L
Linus Torvalds 已提交
242 243
	drm_client_t __user *argp = (void __user *)arg;
	drm_client_t client;
D
Dave Airlie 已提交
244 245 246
	drm_file_t *pt;
	int idx;
	int i;
L
Linus Torvalds 已提交
247 248 249 250 251

	if (copy_from_user(&client, argp, sizeof(client)))
		return -EFAULT;
	idx = client.idx;
	down(&dev->struct_sem);
D
Dave Airlie 已提交
252
	for (i = 0, pt = dev->file_first; i < idx && pt; i++, pt = pt->next) ;
L
Linus Torvalds 已提交
253 254 255 256 257

	if (!pt) {
		up(&dev->struct_sem);
		return -EINVAL;
	}
D
Dave Airlie 已提交
258 259 260
	client.auth = pt->authenticated;
	client.pid = pt->pid;
	client.uid = pt->uid;
L
Linus Torvalds 已提交
261
	client.magic = pt->magic;
D
Dave Airlie 已提交
262
	client.iocs = pt->ioctl_count;
L
Linus Torvalds 已提交
263 264
	up(&dev->struct_sem);

D
Dave Airlie 已提交
265
	if (copy_to_user((drm_client_t __user *) arg, &client, sizeof(client)))
L
Linus Torvalds 已提交
266 267 268 269
		return -EFAULT;
	return 0;
}

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

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

L
Linus Torvalds 已提交
290 291 292 293 294
	down(&dev->struct_sem);

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

L
Linus Torvalds 已提交
301 302 303 304
	stats.count = dev->counters;

	up(&dev->struct_sem);

D
Dave Airlie 已提交
305
	if (copy_to_user((drm_stats_t __user *) arg, &stats, sizeof(stats)))
L
Linus Torvalds 已提交
306 307 308 309 310 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 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
		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_version_t version;

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

	memset(&version, 0, sizeof(version));

	dev->driver->version(&version);
	retv.drm_di_major = DRM_IF_MAJOR;
	retv.drm_di_minor = DRM_IF_MINOR;
	retv.drm_dd_major = version.version_major;
	retv.drm_dd_minor = version.version_minor;

	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;
		if_version = DRM_IF_VERSION(sv.drm_di_major, sv.drm_dd_minor);
		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) {
		if (sv.drm_dd_major != version.version_major ||
D
Dave Airlie 已提交
358 359
		    sv.drm_dd_minor < 0
		    || sv.drm_dd_minor > version.version_minor)
L
Linus Torvalds 已提交
360 361 362 363 364 365 366 367 368 369
			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 已提交
370
	     unsigned long arg)
L
Linus Torvalds 已提交
371 372 373 374
{
	DRM_DEBUG("\n");
	return 0;
}