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