xref: /dragonfly/sys/dev/drm/drm_ioctl.c (revision 61c0377f)
1 /**
2  * \file drm_ioctl.c
3  * IOCTL processing for DRM
4  *
5  * \author Rickard E. (Rik) Faith <faith@valinux.com>
6  * \author Gareth Hughes <gareth@valinux.com>
7  */
8 
9 /*
10  * Created: Fri Jan  8 09:01:26 1999 by faith@valinux.com
11  *
12  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
13  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
14  * All Rights Reserved.
15  *
16  * Permission is hereby granted, free of charge, to any person obtaining a
17  * copy of this software and associated documentation files (the "Software"),
18  * to deal in the Software without restriction, including without limitation
19  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
20  * and/or sell copies of the Software, and to permit persons to whom the
21  * Software is furnished to do so, subject to the following conditions:
22  *
23  * The above copyright notice and this permission notice (including the next
24  * paragraph) shall be included in all copies or substantial portions of the
25  * Software.
26  *
27  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
30  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
31  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
32  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33  * OTHER DEALINGS IN THE SOFTWARE.
34  *
35  * $FreeBSD: src/sys/dev/drm2/drm_ioctl.c,v 1.1 2012/05/22 11:07:44 kib Exp $
36  */
37 
38 #include <drm/drmP.h>
39 #include <drm/drm_core.h>
40 
41 #include <linux/export.h>
42 
43 /**
44  * Get the bus id.
45  *
46  * \param inode device inode.
47  * \param file_priv DRM file private.
48  * \param cmd command.
49  * \param arg user argument, pointing to a drm_unique structure.
50  * \return zero on success or a negative number on failure.
51  *
52  * Copies the bus id from drm_device::unique into user space.
53  */
54 int drm_getunique(struct drm_device *dev, void *data,
55 		  struct drm_file *file_priv)
56 {
57 	struct drm_unique *u = data;
58 
59 	if (u->unique_len >= dev->unique_len) {
60 		if (DRM_COPY_TO_USER(u->unique, dev->unique, dev->unique_len))
61 			return EFAULT;
62 	}
63 	u->unique_len = dev->unique_len;
64 
65 	return 0;
66 }
67 
68 /**
69  * Set the bus id.
70  *
71  * \param inode device inode.
72  * \param file_priv DRM file private.
73  * \param cmd command.
74  * \param arg user argument, pointing to a drm_unique structure.
75  * \return zero on success or a negative number on failure.
76  *
77  * Copies the bus id from userspace into drm_device::unique, and verifies that
78  * it matches the device this DRM is attached to (EINVAL otherwise).  Deprecated
79  * in interface version 1.1 and will return EBUSY when setversion has requested
80  * version 1.1 or greater.
81  */
82 int drm_setunique(struct drm_device *dev, void *data,
83 		  struct drm_file *file_priv)
84 {
85 	struct drm_unique *u = data;
86 	int domain, bus, slot, func, ret;
87 	char *busid;
88 
89 	/* Check and copy in the submitted Bus ID */
90 	if (!u->unique_len || u->unique_len > 1024)
91 		return EINVAL;
92 
93 	busid = kmalloc(u->unique_len + 1, DRM_MEM_DRIVER, M_WAITOK);
94 	if (busid == NULL)
95 		return ENOMEM;
96 
97 	if (DRM_COPY_FROM_USER(busid, u->unique, u->unique_len)) {
98 		drm_free(busid, DRM_MEM_DRIVER);
99 		return EFAULT;
100 	}
101 	busid[u->unique_len] = '\0';
102 
103 	/* Return error if the busid submitted doesn't match the device's actual
104 	 * busid.
105 	 */
106 	ret = ksscanf(busid, "PCI:%d:%d:%d", &bus, &slot, &func);
107 	if (ret != 3) {
108 		drm_free(busid, DRM_MEM_DRIVER);
109 		return EINVAL;
110 	}
111 	domain = bus >> 8;
112 	bus &= 0xff;
113 
114 	if ((domain != dev->pci_domain) ||
115 	    (bus != dev->pci_bus) ||
116 	    (slot != dev->pci_slot) ||
117 	    (func != dev->pci_func)) {
118 		drm_free(busid, DRM_MEM_DRIVER);
119 		return EINVAL;
120 	}
121 
122 	/* Actually set the device's busid now. */
123 	DRM_LOCK(dev);
124 	if (dev->unique_len || dev->unique) {
125 		DRM_UNLOCK(dev);
126 		return EBUSY;
127 	}
128 
129 	dev->unique_len = u->unique_len;
130 	dev->unique = busid;
131 	DRM_UNLOCK(dev);
132 
133 	return 0;
134 }
135 
136 static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
137 {
138 
139 	DRM_LOCK(dev);
140 
141 	dev->unique_len = 20;
142 	dev->unique = kmalloc(dev->unique_len + 1, DRM_MEM_DRIVER, M_NOWAIT);
143 	if (dev->unique == NULL) {
144 		DRM_UNLOCK(dev);
145 		return ENOMEM;
146 	}
147 
148 	ksnprintf(dev->unique, dev->unique_len, "pci:%04x:%02x:%02x.%1x",
149 	    dev->pci_domain, dev->pci_bus, dev->pci_slot, dev->pci_func);
150 
151 	DRM_UNLOCK(dev);
152 
153 	return 0;
154 }
155 
156 /**
157  * Get a mapping information.
158  *
159  * \param inode device inode.
160  * \param file_priv DRM file private.
161  * \param cmd command.
162  * \param arg user argument, pointing to a drm_map structure.
163  *
164  * \return zero on success or a negative number on failure.
165  *
166  * Searches for the mapping with the specified offset and copies its information
167  * into userspace
168  */
169 int drm_getmap(struct drm_device *dev, void *data,
170 	       struct drm_file *file_priv)
171 {
172 	struct drm_map *map = data;
173 	struct drm_map_list *r_list = NULL;
174 	struct list_head *list;
175 	int idx;
176 	int i;
177 
178 	idx = map->offset;
179 	if (idx < 0) {
180 		return EINVAL;
181 	}
182 
183 	i = 0;
184 	DRM_LOCK(dev);
185 	list_for_each(list, &dev->maplist) {
186 		if (i == idx) {
187 			r_list = list_entry(list, struct drm_map_list, head);
188 			break;
189 		}
190 		i++;
191 	}
192 	if (!r_list || !r_list->map) {
193 		DRM_UNLOCK(dev);
194 		return -EINVAL;
195 	}
196 
197 	map->offset = r_list->map->offset;
198 	map->size = r_list->map->size;
199 	map->type = r_list->map->type;
200 	map->flags = r_list->map->flags;
201 	map->handle = r_list->map->handle;
202 	map->mtrr   = r_list->map->mtrr;
203 	DRM_UNLOCK(dev);
204 
205 	return 0;
206 }
207 
208 /**
209  * Get client information.
210  *
211  * \param inode device inode.
212  * \param file_priv DRM file private.
213  * \param cmd command.
214  * \param arg user argument, pointing to a drm_client structure.
215  *
216  * \return zero on success or a negative number on failure.
217  *
218  * Searches for the client with the specified index and copies its information
219  * into userspace
220  */
221 int drm_getclient(struct drm_device *dev, void *data,
222 		  struct drm_file *file_priv)
223 {
224 	struct drm_client *client = data;
225 	struct drm_file *pt;
226 	int idx;
227 	int i = 0;
228 
229 	idx = client->idx;
230 	DRM_LOCK(dev);
231 	list_for_each_entry(pt, &dev->filelist, lhead) {
232 		if (i++ >= idx) {
233 			client->auth  = pt->authenticated;
234 			client->pid   = pt->pid;
235 			client->uid   = pt->uid;
236 			client->magic = pt->magic;
237 			client->iocs  = pt->ioctl_count;
238 			DRM_UNLOCK(dev);
239 
240 			return 0;
241 		}
242 	}
243 	DRM_UNLOCK(dev);
244 
245 	return EINVAL;
246 }
247 
248 /**
249  * Get statistics information.
250  *
251  * \param inode device inode.
252  * \param file_priv DRM file private.
253  * \param cmd command.
254  * \param arg user argument, pointing to a drm_stats structure.
255  *
256  * \return zero on success or a negative number on failure.
257  */
258 int drm_getstats(struct drm_device *dev, void *data, struct drm_file *file_priv)
259 {
260 	struct drm_stats *stats = data;
261 	int          i;
262 
263 	memset(stats, 0, sizeof(struct drm_stats));
264 
265 	DRM_LOCK(dev);
266 
267 	for (i = 0; i < dev->counters; i++) {
268 		if (dev->types[i] == _DRM_STAT_LOCK)
269 			stats->data[i].value =
270 			    (dev->lock.hw_lock ? dev->lock.hw_lock->lock : 0);
271 		else
272 			stats->data[i].value = atomic_read(&dev->counts[i]);
273 		stats->data[i].type = dev->types[i];
274 	}
275 
276 	stats->count = dev->counters;
277 
278 	DRM_UNLOCK(dev);
279 
280 	return 0;
281 }
282 
283 /**
284  * Get device/driver capabilities
285  */
286 int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
287 {
288 	struct drm_get_cap *req = data;
289 
290 	req->value = 0;
291 	switch (req->capability) {
292 	case DRM_CAP_DUMB_BUFFER:
293 		if (dev->driver->dumb_create)
294 			req->value = 1;
295 		break;
296 	case DRM_CAP_VBLANK_HIGH_CRTC:
297 		req->value = 1;
298 		break;
299 	case DRM_CAP_DUMB_PREFERRED_DEPTH:
300 		req->value = dev->mode_config.preferred_depth;
301 		break;
302 	case DRM_CAP_DUMB_PREFER_SHADOW:
303 		req->value = dev->mode_config.prefer_shadow;
304 		break;
305 	case DRM_CAP_TIMESTAMP_MONOTONIC:
306 		req->value = drm_timestamp_monotonic;
307 		break;
308 	default:
309 		return EINVAL;
310 	}
311 	return 0;
312 }
313 
314 /**
315  * Setversion ioctl.
316  *
317  * \param inode device inode.
318  * \param file_priv DRM file private.
319  * \param cmd command.
320  * \param arg user argument, pointing to a drm_lock structure.
321  * \return zero on success or negative number on failure.
322  *
323  * Sets the requested interface version
324  */
325 int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv)
326 {
327 	struct drm_set_version *sv = data;
328 	struct drm_set_version ver;
329 	int if_version, retcode = 0;
330 
331 	/* Save the incoming data, and set the response before continuing
332 	 * any further.
333 	 */
334 	ver = *sv;
335 	sv->drm_di_major = DRM_IF_MAJOR;
336 	sv->drm_di_minor = DRM_IF_MINOR;
337 	sv->drm_dd_major = dev->driver->major;
338 	sv->drm_dd_minor = dev->driver->minor;
339 
340 	if (ver.drm_di_major != -1) {
341 		if (ver.drm_di_major != DRM_IF_MAJOR ||
342 		    ver.drm_di_minor < 0 || ver.drm_di_minor > DRM_IF_MINOR) {
343 			return EINVAL;
344 		}
345 		if_version = DRM_IF_VERSION(ver.drm_di_major,
346 		    ver.drm_dd_minor);
347 		dev->if_version = DRM_MAX(if_version, dev->if_version);
348 		if (ver.drm_di_minor >= 1) {
349 			/*
350 			 * Version 1.1 includes tying of DRM to specific device
351 			 * Version 1.4 has proper PCI domain support
352 			 */
353 			retcode = drm_set_busid(dev, file_priv);
354 			if (retcode)
355 				return retcode;
356 		}
357 	}
358 
359 	if (ver.drm_dd_major != -1) {
360 		if (ver.drm_dd_major != dev->driver->major ||
361 		    ver.drm_dd_minor < 0 ||
362 		    ver.drm_dd_minor > dev->driver->minor)
363 		{
364 			return EINVAL;
365 		}
366 	}
367 
368 	return 0;
369 }
370 
371 /** No-op ioctl. */
372 int drm_noop(struct drm_device *dev, void *data,
373 	     struct drm_file *file_priv)
374 {
375 	DRM_DEBUG("\n");
376 	return 0;
377 }
378 EXPORT_SYMBOL(drm_noop);
379