12490Sjkh/*-
22490Sjkh * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
32490Sjkh * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
42490Sjkh * All Rights Reserved.
52490Sjkh *
62490Sjkh * Permission is hereby granted, free of charge, to any person obtaining a
72490Sjkh * copy of this software and associated documentation files (the "Software"),
82490Sjkh * to deal in the Software without restriction, including without limitation
92490Sjkh * the rights to use, copy, modify, merge, publish, distribute, sublicense,
102490Sjkh * and/or sell copies of the Software, and to permit persons to whom the
112490Sjkh * Software is furnished to do so, subject to the following conditions:
122490Sjkh *
132490Sjkh * The above copyright notice and this permission notice (including the next
142490Sjkh * paragraph) shall be included in all copies or substantial portions of the
152490Sjkh * Software.
162490Sjkh *
172490Sjkh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
182490Sjkh * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
192490Sjkh * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
202490Sjkh * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
212490Sjkh * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
222490Sjkh * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
232490Sjkh * OTHER DEALINGS IN THE SOFTWARE.
242490Sjkh *
252490Sjkh * Authors:
262490Sjkh *    Rickard E. (Rik) Faith <faith@valinux.com>
272490Sjkh *    Gareth Hughes <gareth@valinux.com>
282490Sjkh *
292490Sjkh */
302490Sjkh
312490Sjkh#include <sys/cdefs.h>
322490Sjkh__FBSDID("$FreeBSD$");
332490Sjkh
342490Sjkh/** @file drm_ioctl.c
352490Sjkh * Varios minor DRM ioctls not applicable to other files, such as versioning
362490Sjkh * information and reporting DRM information to userland.
372490Sjkh */
382490Sjkh
392490Sjkh#include <dev/drm2/drmP.h>
402490Sjkh#include <dev/drm2/drm_core.h>
412490Sjkh
422490Sjkh/*
432490Sjkh * Beginning in revision 1.1 of the DRM interface, getunique will return
442490Sjkh * a unique in the form pci:oooo:bb:dd.f (o=domain, b=bus, d=device, f=function)
452490Sjkh * before setunique has been called.  The format for the bus-specific part of
462490Sjkh * the unique is not defined for any other bus.
472490Sjkh */
482490Sjkhint drm_getunique(struct drm_device *dev, void *data,
492490Sjkh		  struct drm_file *file_priv)
502490Sjkh{
512490Sjkh	struct drm_unique *u = data;
522490Sjkh
532490Sjkh	if (u->unique_len >= dev->unique_len) {
542490Sjkh		if (DRM_COPY_TO_USER(u->unique, dev->unique, dev->unique_len))
552490Sjkh			return EFAULT;
562490Sjkh	}
572490Sjkh	u->unique_len = dev->unique_len;
582490Sjkh
592490Sjkh	return 0;
602490Sjkh}
612490Sjkh
622490Sjkh/* Deprecated in DRM version 1.1, and will return EBUSY when setversion has
632490Sjkh * requested version 1.1 or greater.
642490Sjkh */
652490Sjkhint drm_setunique(struct drm_device *dev, void *data,
662490Sjkh		  struct drm_file *file_priv)
672490Sjkh{
682490Sjkh	struct drm_unique *u = data;
692490Sjkh	int domain, bus, slot, func, ret;
702490Sjkh	char *busid;
712490Sjkh
722490Sjkh	/* Check and copy in the submitted Bus ID */
732490Sjkh	if (!u->unique_len || u->unique_len > 1024)
742490Sjkh		return EINVAL;
752490Sjkh
762490Sjkh	busid = malloc(u->unique_len + 1, DRM_MEM_DRIVER, M_WAITOK);
772490Sjkh	if (busid == NULL)
788856Srgrimes		return ENOMEM;
792490Sjkh
802490Sjkh	if (DRM_COPY_FROM_USER(busid, u->unique, u->unique_len)) {
812490Sjkh		free(busid, DRM_MEM_DRIVER);
822490Sjkh		return EFAULT;
832490Sjkh	}
842490Sjkh	busid[u->unique_len] = '\0';
852490Sjkh
862490Sjkh	/* Return error if the busid submitted doesn't match the device's actual
872490Sjkh	 * busid.
882490Sjkh	 */
892490Sjkh	ret = sscanf(busid, "PCI:%d:%d:%d", &bus, &slot, &func);
902490Sjkh	if (ret != 3) {
912490Sjkh		free(busid, DRM_MEM_DRIVER);
922490Sjkh		return EINVAL;
932490Sjkh	}
942490Sjkh	domain = bus >> 8;
952490Sjkh	bus &= 0xff;
962490Sjkh
972490Sjkh	if ((domain != dev->pci_domain) ||
982490Sjkh	    (bus != dev->pci_bus) ||
992490Sjkh	    (slot != dev->pci_slot) ||
1002490Sjkh	    (func != dev->pci_func)) {
1012490Sjkh		free(busid, DRM_MEM_DRIVER);
1022490Sjkh		return EINVAL;
1032490Sjkh	}
1042490Sjkh
1052490Sjkh	/* Actually set the device's busid now. */
1062490Sjkh	DRM_LOCK(dev);
1072490Sjkh	if (dev->unique_len || dev->unique) {
1082490Sjkh		DRM_UNLOCK(dev);
1092490Sjkh		return EBUSY;
1102490Sjkh	}
1112490Sjkh
1122490Sjkh	dev->unique_len = u->unique_len;
1132490Sjkh	dev->unique = busid;
1142490Sjkh	DRM_UNLOCK(dev);
1152490Sjkh
1162490Sjkh	return 0;
1172490Sjkh}
1182490Sjkh
1192490Sjkh
1202490Sjkhstatic int
1212490Sjkhdrm_set_busid(struct drm_device *dev)
1222490Sjkh{
1232490Sjkh
1242490Sjkh	DRM_LOCK(dev);
1252490Sjkh
1262490Sjkh	if (dev->unique != NULL) {
1272490Sjkh		DRM_UNLOCK(dev);
1282490Sjkh		return EBUSY;
1292490Sjkh	}
1302490Sjkh
1312490Sjkh	dev->unique_len = 20;
1322490Sjkh	dev->unique = malloc(dev->unique_len + 1, DRM_MEM_DRIVER, M_NOWAIT);
1332490Sjkh	if (dev->unique == NULL) {
1342490Sjkh		DRM_UNLOCK(dev);
1352490Sjkh		return ENOMEM;
1362490Sjkh	}
1372490Sjkh
1382490Sjkh	snprintf(dev->unique, dev->unique_len, "pci:%04x:%02x:%02x.%1x",
1392490Sjkh	    dev->pci_domain, dev->pci_bus, dev->pci_slot, dev->pci_func);
1402490Sjkh
1412490Sjkh	DRM_UNLOCK(dev);
1422490Sjkh
1432490Sjkh	return 0;
1442490Sjkh}
1452490Sjkh
1462490Sjkhint drm_getmap(struct drm_device *dev, void *data, struct drm_file *file_priv)
1472490Sjkh{
1482490Sjkh	struct drm_map     *map = data;
1492490Sjkh	drm_local_map_t    *mapinlist;
1502490Sjkh	int          idx;
1512490Sjkh	int	     i = 0;
1522490Sjkh
1532490Sjkh	idx = map->offset;
1542490Sjkh
1552490Sjkh	DRM_LOCK(dev);
1562490Sjkh	if (idx < 0) {
1572490Sjkh		DRM_UNLOCK(dev);
1582490Sjkh		return EINVAL;
1592490Sjkh	}
1602490Sjkh
1612490Sjkh	TAILQ_FOREACH(mapinlist, &dev->maplist, link) {
1622490Sjkh		if (i == idx) {
1632490Sjkh			map->offset = mapinlist->offset;
1642490Sjkh			map->size   = mapinlist->size;
1652490Sjkh			map->type   = mapinlist->type;
1662490Sjkh			map->flags  = mapinlist->flags;
1672490Sjkh			map->handle = mapinlist->handle;
1682490Sjkh			map->mtrr   = mapinlist->mtrr;
1692490Sjkh			break;
1702490Sjkh		}
1712490Sjkh		i++;
1722490Sjkh	}
1732490Sjkh
1742490Sjkh	DRM_UNLOCK(dev);
1752490Sjkh
1762490Sjkh 	if (mapinlist == NULL)
1772490Sjkh		return EINVAL;
1782490Sjkh
1792490Sjkh	return 0;
1802490Sjkh}
1812490Sjkh
1822490Sjkhint drm_getclient(struct drm_device *dev, void *data,
1832490Sjkh		  struct drm_file *file_priv)
1842490Sjkh{
1852490Sjkh	struct drm_client *client = data;
1862490Sjkh	struct drm_file *pt;
1872490Sjkh	int idx;
1882490Sjkh	int i = 0;
1892490Sjkh
1902490Sjkh	idx = client->idx;
1912490Sjkh	DRM_LOCK(dev);
1922490Sjkh	TAILQ_FOREACH(pt, &dev->files, link) {
1932490Sjkh		if (i == idx) {
1942490Sjkh			client->auth  = pt->authenticated;
1952490Sjkh			client->pid   = pt->pid;
1962490Sjkh			client->uid   = pt->uid;
1972490Sjkh			client->magic = pt->magic;
1982490Sjkh			client->iocs  = pt->ioctl_count;
1992490Sjkh			DRM_UNLOCK(dev);
2002490Sjkh			return 0;
2012490Sjkh		}
202		i++;
203	}
204	DRM_UNLOCK(dev);
205
206	return EINVAL;
207}
208
209int drm_getstats(struct drm_device *dev, void *data, struct drm_file *file_priv)
210{
211	struct drm_stats *stats = data;
212	int          i;
213
214	memset(stats, 0, sizeof(struct drm_stats));
215
216	DRM_LOCK(dev);
217
218	for (i = 0; i < dev->counters; i++) {
219		if (dev->types[i] == _DRM_STAT_LOCK)
220			stats->data[i].value =
221			    (dev->lock.hw_lock ? dev->lock.hw_lock->lock : 0);
222		else
223			stats->data[i].value = atomic_read(&dev->counts[i]);
224		stats->data[i].type = dev->types[i];
225	}
226
227	stats->count = dev->counters;
228
229	DRM_UNLOCK(dev);
230
231	return 0;
232}
233
234int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
235{
236	struct drm_get_cap *req = data;
237
238	req->value = 0;
239	switch (req->capability) {
240	case DRM_CAP_DUMB_BUFFER:
241		if (dev->driver->dumb_create)
242			req->value = 1;
243		break;
244	case DRM_CAP_VBLANK_HIGH_CRTC:
245		req->value = 1;
246		break;
247	case DRM_CAP_DUMB_PREFERRED_DEPTH:
248		req->value = dev->mode_config.preferred_depth;
249		break;
250	case DRM_CAP_DUMB_PREFER_SHADOW:
251		req->value = dev->mode_config.prefer_shadow;
252		break;
253	case DRM_CAP_TIMESTAMP_MONOTONIC:
254		req->value = drm_timestamp_monotonic;
255		break;
256	default:
257		return EINVAL;
258	}
259	return 0;
260}
261
262int drm_setversion(struct drm_device *dev, void *data,
263		   struct drm_file *file_priv)
264{
265	struct drm_set_version *sv = data;
266	struct drm_set_version ver;
267	int if_version;
268
269	/* Save the incoming data, and set the response before continuing
270	 * any further.
271	 */
272	ver = *sv;
273	sv->drm_di_major = DRM_IF_MAJOR;
274	sv->drm_di_minor = DRM_IF_MINOR;
275	sv->drm_dd_major = dev->driver->major;
276	sv->drm_dd_minor = dev->driver->minor;
277
278	DRM_DEBUG("ver.drm_di_major %d ver.drm_di_minor %d "
279	    "ver.drm_dd_major %d ver.drm_dd_minor %d\n",
280	    ver.drm_di_major, ver.drm_di_minor, ver.drm_dd_major,
281	    ver.drm_dd_minor);
282	DRM_DEBUG("sv->drm_di_major %d sv->drm_di_minor %d "
283	    "sv->drm_dd_major %d sv->drm_dd_minor %d\n",
284	    sv->drm_di_major, sv->drm_di_minor, sv->drm_dd_major,
285	    sv->drm_dd_minor);
286
287	if (ver.drm_di_major != -1) {
288		if (ver.drm_di_major != DRM_IF_MAJOR ||
289		    ver.drm_di_minor < 0 || ver.drm_di_minor > DRM_IF_MINOR) {
290			return EINVAL;
291		}
292		if_version = DRM_IF_VERSION(ver.drm_di_major,
293		    ver.drm_dd_minor);
294		dev->if_version = DRM_MAX(if_version, dev->if_version);
295		if (ver.drm_di_minor >= 1) {
296			/*
297			 * Version 1.1 includes tying of DRM to specific device
298			 */
299			drm_set_busid(dev);
300		}
301	}
302
303	if (ver.drm_dd_major != -1) {
304		if (ver.drm_dd_major != dev->driver->major ||
305		    ver.drm_dd_minor < 0 ||
306		    ver.drm_dd_minor > dev->driver->minor)
307		{
308			return EINVAL;
309		}
310	}
311
312	return 0;
313}
314
315
316int drm_noop(struct drm_device *dev, void *data, struct drm_file *file_priv)
317{
318	DRM_DEBUG("\n");
319	return 0;
320}
321