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