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