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