xref: /dragonfly/sys/dev/drm/drm_ioctl.c (revision b0d289c2)
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, M_DRM, 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, M_DRM);
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, M_DRM);
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, M_DRM);
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, M_DRM, M_WAITOK | M_NULLOK);
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  * Set device/driver capabilities
316  */
317 int
318 drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
319 {
320 	struct drm_set_client_cap *req = data;
321 
322 	switch (req->capability) {
323 	case DRM_CLIENT_CAP_STEREO_3D:
324 		if (req->value > 1)
325 			return -EINVAL;
326 		file_priv->stereo_allowed = req->value;
327 		break;
328 	default:
329 		return -EINVAL;
330 	}
331 
332 	return 0;
333 }
334 
335 /**
336  * Setversion ioctl.
337  *
338  * \param inode device inode.
339  * \param file_priv DRM file private.
340  * \param cmd command.
341  * \param arg user argument, pointing to a drm_lock structure.
342  * \return zero on success or negative number on failure.
343  *
344  * Sets the requested interface version
345  */
346 int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv)
347 {
348 	struct drm_set_version *sv = data;
349 	struct drm_set_version ver;
350 	int if_version, retcode = 0;
351 
352 	/* Save the incoming data, and set the response before continuing
353 	 * any further.
354 	 */
355 	ver = *sv;
356 	sv->drm_di_major = DRM_IF_MAJOR;
357 	sv->drm_di_minor = DRM_IF_MINOR;
358 	sv->drm_dd_major = dev->driver->major;
359 	sv->drm_dd_minor = dev->driver->minor;
360 
361 	if (ver.drm_di_major != -1) {
362 		if (ver.drm_di_major != DRM_IF_MAJOR ||
363 		    ver.drm_di_minor < 0 || ver.drm_di_minor > DRM_IF_MINOR) {
364 			return EINVAL;
365 		}
366 		if_version = DRM_IF_VERSION(ver.drm_di_major,
367 		    ver.drm_dd_minor);
368 		dev->if_version = DRM_MAX(if_version, dev->if_version);
369 		if (ver.drm_di_minor >= 1) {
370 			/*
371 			 * Version 1.1 includes tying of DRM to specific device
372 			 * Version 1.4 has proper PCI domain support
373 			 */
374 			retcode = drm_set_busid(dev, file_priv);
375 			if (retcode)
376 				return retcode;
377 		}
378 	}
379 
380 	if (ver.drm_dd_major != -1) {
381 		if (ver.drm_dd_major != dev->driver->major ||
382 		    ver.drm_dd_minor < 0 ||
383 		    ver.drm_dd_minor > dev->driver->minor)
384 		{
385 			return EINVAL;
386 		}
387 	}
388 
389 	return 0;
390 }
391 
392 /** No-op ioctl. */
393 int drm_noop(struct drm_device *dev, void *data,
394 	     struct drm_file *file_priv)
395 {
396 	DRM_DEBUG("\n");
397 	return 0;
398 }
399 EXPORT_SYMBOL(drm_noop);
400