1280183Sdumbbell/**
2280183Sdumbbell * \file drm_ioctl.c
3280183Sdumbbell * IOCTL processing for DRM
4280183Sdumbbell *
5280183Sdumbbell * \author Rickard E. (Rik) Faith <faith@valinux.com>
6280183Sdumbbell * \author Gareth Hughes <gareth@valinux.com>
7280183Sdumbbell */
8280183Sdumbbell
9280183Sdumbbell/*
10280183Sdumbbell * Created: Fri Jan  8 09:01:26 1999 by faith@valinux.com
11280183Sdumbbell *
12235783Skib * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
13235783Skib * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
14235783Skib * All Rights Reserved.
15235783Skib *
16235783Skib * Permission is hereby granted, free of charge, to any person obtaining a
17235783Skib * copy of this software and associated documentation files (the "Software"),
18235783Skib * to deal in the Software without restriction, including without limitation
19235783Skib * the rights to use, copy, modify, merge, publish, distribute, sublicense,
20235783Skib * and/or sell copies of the Software, and to permit persons to whom the
21235783Skib * Software is furnished to do so, subject to the following conditions:
22235783Skib *
23235783Skib * The above copyright notice and this permission notice (including the next
24235783Skib * paragraph) shall be included in all copies or substantial portions of the
25235783Skib * Software.
26235783Skib *
27235783Skib * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28235783Skib * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29235783Skib * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
30235783Skib * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
31235783Skib * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
32235783Skib * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33235783Skib * OTHER DEALINGS IN THE SOFTWARE.
34235783Skib */
35235783Skib
36235783Skib#include <sys/cdefs.h>
37235783Skib__FBSDID("$FreeBSD$");
38235783Skib
39235783Skib#include <dev/drm2/drmP.h>
40254840Sdumbbell#include <dev/drm2/drm_core.h>
41235783Skib
42280183Sdumbbell/**
43280183Sdumbbell * Get the bus id.
44280183Sdumbbell *
45280183Sdumbbell * \param inode device inode.
46280183Sdumbbell * \param file_priv DRM file private.
47280183Sdumbbell * \param cmd command.
48280183Sdumbbell * \param arg user argument, pointing to a drm_unique structure.
49280183Sdumbbell * \return zero on success or a negative number on failure.
50280183Sdumbbell *
51280183Sdumbbell * Copies the bus id from drm_device::unique into user space.
52235783Skib */
53235783Skibint drm_getunique(struct drm_device *dev, void *data,
54235783Skib		  struct drm_file *file_priv)
55235783Skib{
56235783Skib	struct drm_unique *u = data;
57280183Sdumbbell	struct drm_master *master = file_priv->master;
58235783Skib
59280183Sdumbbell	if (u->unique_len >= master->unique_len) {
60280183Sdumbbell		if (copy_to_user(u->unique, master->unique, master->unique_len))
61280183Sdumbbell			return -EFAULT;
62235783Skib	}
63280183Sdumbbell	u->unique_len = master->unique_len;
64235783Skib
65235783Skib	return 0;
66235783Skib}
67235783Skib
68280183Sdumbbellstatic void
69280183Sdumbbelldrm_unset_busid(struct drm_device *dev,
70280183Sdumbbell		struct drm_master *master)
71280183Sdumbbell{
72280183Sdumbbell
73280183Sdumbbell	free(master->unique, DRM_MEM_DRIVER);
74280183Sdumbbell	master->unique = NULL;
75280183Sdumbbell	master->unique_len = 0;
76280183Sdumbbell	master->unique_size = 0;
77280183Sdumbbell}
78280183Sdumbbell
79280183Sdumbbell/**
80280183Sdumbbell * Set the bus id.
81280183Sdumbbell *
82280183Sdumbbell * \param inode device inode.
83280183Sdumbbell * \param file_priv DRM file private.
84280183Sdumbbell * \param cmd command.
85280183Sdumbbell * \param arg user argument, pointing to a drm_unique structure.
86280183Sdumbbell * \return zero on success or a negative number on failure.
87280183Sdumbbell *
88280183Sdumbbell * Copies the bus id from userspace into drm_device::unique, and verifies that
89280183Sdumbbell * it matches the device this DRM is attached to (EINVAL otherwise).  Deprecated
90280183Sdumbbell * in interface version 1.1 and will return EBUSY when setversion has requested
91280183Sdumbbell * version 1.1 or greater.
92235783Skib */
93235783Skibint drm_setunique(struct drm_device *dev, void *data,
94235783Skib		  struct drm_file *file_priv)
95235783Skib{
96235783Skib	struct drm_unique *u = data;
97280183Sdumbbell	struct drm_master *master = file_priv->master;
98280183Sdumbbell	int ret;
99235783Skib
100280183Sdumbbell	if (master->unique_len || master->unique)
101280183Sdumbbell		return -EBUSY;
102280183Sdumbbell
103235783Skib	if (!u->unique_len || u->unique_len > 1024)
104280183Sdumbbell		return -EINVAL;
105235783Skib
106280183Sdumbbell	if (!dev->driver->bus->set_unique)
107280183Sdumbbell		return -EINVAL;
108235783Skib
109280183Sdumbbell	ret = dev->driver->bus->set_unique(dev, master, u);
110280183Sdumbbell	if (ret)
111280183Sdumbbell		goto err;
112235783Skib
113280183Sdumbbell	return 0;
114235783Skib
115280183Sdumbbellerr:
116280183Sdumbbell	drm_unset_busid(dev, master);
117280183Sdumbbell	return ret;
118235783Skib}
119235783Skib
120280183Sdumbbellstatic int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
121235783Skib{
122280183Sdumbbell	struct drm_master *master = file_priv->master;
123280183Sdumbbell	int ret;
124235783Skib
125280183Sdumbbell	if (master->unique != NULL)
126280183Sdumbbell		drm_unset_busid(dev, master);
127235783Skib
128280183Sdumbbell	ret = dev->driver->bus->set_busid(dev, master);
129280183Sdumbbell	if (ret)
130280183Sdumbbell		goto err;
131235783Skib	return 0;
132280183Sdumbbellerr:
133280183Sdumbbell	drm_unset_busid(dev, master);
134280183Sdumbbell	return ret;
135235783Skib}
136235783Skib
137280183Sdumbbell/**
138280183Sdumbbell * Get a mapping information.
139280183Sdumbbell *
140280183Sdumbbell * \param inode device inode.
141280183Sdumbbell * \param file_priv DRM file private.
142280183Sdumbbell * \param cmd command.
143280183Sdumbbell * \param arg user argument, pointing to a drm_map structure.
144280183Sdumbbell *
145280183Sdumbbell * \return zero on success or a negative number on failure.
146280183Sdumbbell *
147280183Sdumbbell * Searches for the mapping with the specified offset and copies its information
148280183Sdumbbell * into userspace
149280183Sdumbbell */
150280183Sdumbbellint drm_getmap(struct drm_device *dev, void *data,
151280183Sdumbbell	       struct drm_file *file_priv)
152235783Skib{
153280183Sdumbbell	struct drm_map *map = data;
154280183Sdumbbell	struct drm_map_list *r_list = NULL;
155280183Sdumbbell	struct list_head *list;
156280183Sdumbbell	int idx;
157280183Sdumbbell	int i;
158235783Skib
159235783Skib	idx = map->offset;
160280183Sdumbbell	if (idx < 0)
161280183Sdumbbell		return -EINVAL;
162235783Skib
163280183Sdumbbell	i = 0;
164235783Skib	DRM_LOCK(dev);
165280183Sdumbbell	list_for_each(list, &dev->maplist) {
166235783Skib		if (i == idx) {
167280183Sdumbbell			r_list = list_entry(list, struct drm_map_list, head);
168235783Skib			break;
169235783Skib		}
170235783Skib		i++;
171235783Skib	}
172280183Sdumbbell	if (!r_list || !r_list->map) {
173280183Sdumbbell		DRM_UNLOCK(dev);
174280183Sdumbbell		return -EINVAL;
175280183Sdumbbell	}
176235783Skib
177280183Sdumbbell	map->offset = r_list->map->offset;
178280183Sdumbbell	map->size = r_list->map->size;
179280183Sdumbbell	map->type = r_list->map->type;
180280183Sdumbbell	map->flags = r_list->map->flags;
181280183Sdumbbell	map->handle = (void *)(unsigned long) r_list->user_token;
182280183Sdumbbell	map->mtrr = r_list->map->mtrr;
183235783Skib	DRM_UNLOCK(dev);
184235783Skib
185235783Skib	return 0;
186235783Skib}
187235783Skib
188280183Sdumbbell/**
189280183Sdumbbell * Get client information.
190280183Sdumbbell *
191280183Sdumbbell * \param inode device inode.
192280183Sdumbbell * \param file_priv DRM file private.
193280183Sdumbbell * \param cmd command.
194280183Sdumbbell * \param arg user argument, pointing to a drm_client structure.
195280183Sdumbbell *
196280183Sdumbbell * \return zero on success or a negative number on failure.
197280183Sdumbbell *
198280183Sdumbbell * Searches for the client with the specified index and copies its information
199280183Sdumbbell * into userspace
200280183Sdumbbell */
201235783Skibint drm_getclient(struct drm_device *dev, void *data,
202235783Skib		  struct drm_file *file_priv)
203235783Skib{
204235783Skib	struct drm_client *client = data;
205235783Skib	struct drm_file *pt;
206235783Skib	int idx;
207280183Sdumbbell	int i;
208235783Skib
209235783Skib	idx = client->idx;
210280183Sdumbbell	i = 0;
211280183Sdumbbell
212235783Skib	DRM_LOCK(dev);
213280183Sdumbbell	list_for_each_entry(pt, &dev->filelist, lhead) {
214280183Sdumbbell		if (i++ >= idx) {
215280183Sdumbbell			client->auth = pt->authenticated;
216280183Sdumbbell			client->pid = pt->pid;
217280183Sdumbbell			client->uid = pt->uid;
218235783Skib			client->magic = pt->magic;
219280183Sdumbbell			client->iocs = pt->ioctl_count;
220235783Skib			DRM_UNLOCK(dev);
221280183Sdumbbell
222235783Skib			return 0;
223235783Skib		}
224235783Skib	}
225235783Skib	DRM_UNLOCK(dev);
226235783Skib
227280183Sdumbbell	return -EINVAL;
228235783Skib}
229235783Skib
230280183Sdumbbell/**
231280183Sdumbbell * Get statistics information.
232280183Sdumbbell *
233280183Sdumbbell * \param inode device inode.
234280183Sdumbbell * \param file_priv DRM file private.
235280183Sdumbbell * \param cmd command.
236280183Sdumbbell * \param arg user argument, pointing to a drm_stats structure.
237280183Sdumbbell *
238280183Sdumbbell * \return zero on success or a negative number on failure.
239280183Sdumbbell */
240280183Sdumbbellint drm_getstats(struct drm_device *dev, void *data,
241280183Sdumbbell		 struct drm_file *file_priv)
242235783Skib{
243235783Skib	struct drm_stats *stats = data;
244280183Sdumbbell	int i;
245235783Skib
246280183Sdumbbell	memset(stats, 0, sizeof(*stats));
247235783Skib
248235783Skib	for (i = 0; i < dev->counters; i++) {
249235783Skib		if (dev->types[i] == _DRM_STAT_LOCK)
250235783Skib			stats->data[i].value =
251280183Sdumbbell			    (file_priv->master->lock.hw_lock ? file_priv->master->lock.hw_lock->lock : 0);
252280183Sdumbbell		else
253235783Skib			stats->data[i].value = atomic_read(&dev->counts[i]);
254235783Skib		stats->data[i].type = dev->types[i];
255235783Skib	}
256280183Sdumbbell
257235783Skib	stats->count = dev->counters;
258235783Skib
259235783Skib	return 0;
260235783Skib}
261235783Skib
262280183Sdumbbell/**
263280183Sdumbbell * Get device/driver capabilities
264280183Sdumbbell */
265235783Skibint drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
266235783Skib{
267235783Skib	struct drm_get_cap *req = data;
268235783Skib
269235783Skib	req->value = 0;
270235783Skib	switch (req->capability) {
271235783Skib	case DRM_CAP_DUMB_BUFFER:
272235783Skib		if (dev->driver->dumb_create)
273235783Skib			req->value = 1;
274235783Skib		break;
275235783Skib	case DRM_CAP_VBLANK_HIGH_CRTC:
276235783Skib		req->value = 1;
277235783Skib		break;
278235783Skib	case DRM_CAP_DUMB_PREFERRED_DEPTH:
279235783Skib		req->value = dev->mode_config.preferred_depth;
280235783Skib		break;
281235783Skib	case DRM_CAP_DUMB_PREFER_SHADOW:
282235783Skib		req->value = dev->mode_config.prefer_shadow;
283235783Skib		break;
284277487Skib	case DRM_CAP_PRIME:
285277487Skib		req->value |= false /* XXXKIB dev->driver->prime_fd_to_handle */ ? DRM_PRIME_CAP_IMPORT : 0;
286277487Skib		req->value |= false /* XXXKIB dev->driver->prime_handle_to_fd */ ? DRM_PRIME_CAP_EXPORT : 0;
287277487Skib		break;
288258262Sdumbbell	case DRM_CAP_TIMESTAMP_MONOTONIC:
289258262Sdumbbell		req->value = drm_timestamp_monotonic;
290258262Sdumbbell		break;
291235783Skib	default:
292280183Sdumbbell		return -EINVAL;
293235783Skib	}
294235783Skib	return 0;
295235783Skib}
296235783Skib
297280183Sdumbbell/**
298280183Sdumbbell * Setversion ioctl.
299280183Sdumbbell *
300280183Sdumbbell * \param inode device inode.
301280183Sdumbbell * \param file_priv DRM file private.
302280183Sdumbbell * \param cmd command.
303280183Sdumbbell * \param arg user argument, pointing to a drm_lock structure.
304280183Sdumbbell * \return zero on success or negative number on failure.
305280183Sdumbbell *
306280183Sdumbbell * Sets the requested interface version
307280183Sdumbbell */
308280183Sdumbbellint drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv)
309235783Skib{
310235783Skib	struct drm_set_version *sv = data;
311280183Sdumbbell	int if_version, retcode = 0;
312235783Skib
313280183Sdumbbell	if (sv->drm_di_major != -1) {
314280183Sdumbbell		if (sv->drm_di_major != DRM_IF_MAJOR ||
315280183Sdumbbell		    sv->drm_di_minor < 0 || sv->drm_di_minor > DRM_IF_MINOR) {
316280183Sdumbbell			retcode = -EINVAL;
317280183Sdumbbell			goto done;
318235783Skib		}
319280183Sdumbbell		if_version = DRM_IF_VERSION(sv->drm_di_major,
320280183Sdumbbell					    sv->drm_di_minor);
321280183Sdumbbell		dev->if_version = max(if_version, dev->if_version);
322280183Sdumbbell		if (sv->drm_di_minor >= 1) {
323235783Skib			/*
324235783Skib			 * Version 1.1 includes tying of DRM to specific device
325280183Sdumbbell			 * Version 1.4 has proper PCI domain support
326235783Skib			 */
327280183Sdumbbell			retcode = drm_set_busid(dev, file_priv);
328280183Sdumbbell			if (retcode)
329280183Sdumbbell				goto done;
330235783Skib		}
331235783Skib	}
332235783Skib
333280183Sdumbbell	if (sv->drm_dd_major != -1) {
334280183Sdumbbell		if (sv->drm_dd_major != dev->driver->major ||
335280183Sdumbbell		    sv->drm_dd_minor < 0 || sv->drm_dd_minor >
336280183Sdumbbell		    dev->driver->minor) {
337280183Sdumbbell			retcode = -EINVAL;
338280183Sdumbbell			goto done;
339235783Skib		}
340280183Sdumbbell
341280183Sdumbbell		if (dev->driver->set_version)
342280183Sdumbbell			dev->driver->set_version(dev, sv);
343235783Skib	}
344235783Skib
345280183Sdumbbelldone:
346280183Sdumbbell	sv->drm_di_major = DRM_IF_MAJOR;
347280183Sdumbbell	sv->drm_di_minor = DRM_IF_MINOR;
348280183Sdumbbell	sv->drm_dd_major = dev->driver->major;
349280183Sdumbbell	sv->drm_dd_minor = dev->driver->minor;
350280183Sdumbbell
351280183Sdumbbell	return retcode;
352235783Skib}
353235783Skib
354280183Sdumbbell/** No-op ioctl. */
355280183Sdumbbellint drm_noop(struct drm_device *dev, void *data,
356280183Sdumbbell	     struct drm_file *file_priv)
357235783Skib{
358235783Skib	DRM_DEBUG("\n");
359235783Skib	return 0;
360235783Skib}
361280183SdumbbellEXPORT_SYMBOL(drm_noop);
362