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_NOWAIT); 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 * Setversion ioctl. 316 * 317 * \param inode device inode. 318 * \param file_priv DRM file private. 319 * \param cmd command. 320 * \param arg user argument, pointing to a drm_lock structure. 321 * \return zero on success or negative number on failure. 322 * 323 * Sets the requested interface version 324 */ 325 int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv) 326 { 327 struct drm_set_version *sv = data; 328 struct drm_set_version ver; 329 int if_version, retcode = 0; 330 331 /* Save the incoming data, and set the response before continuing 332 * any further. 333 */ 334 ver = *sv; 335 sv->drm_di_major = DRM_IF_MAJOR; 336 sv->drm_di_minor = DRM_IF_MINOR; 337 sv->drm_dd_major = dev->driver->major; 338 sv->drm_dd_minor = dev->driver->minor; 339 340 if (ver.drm_di_major != -1) { 341 if (ver.drm_di_major != DRM_IF_MAJOR || 342 ver.drm_di_minor < 0 || ver.drm_di_minor > DRM_IF_MINOR) { 343 return EINVAL; 344 } 345 if_version = DRM_IF_VERSION(ver.drm_di_major, 346 ver.drm_dd_minor); 347 dev->if_version = DRM_MAX(if_version, dev->if_version); 348 if (ver.drm_di_minor >= 1) { 349 /* 350 * Version 1.1 includes tying of DRM to specific device 351 * Version 1.4 has proper PCI domain support 352 */ 353 retcode = drm_set_busid(dev, file_priv); 354 if (retcode) 355 return retcode; 356 } 357 } 358 359 if (ver.drm_dd_major != -1) { 360 if (ver.drm_dd_major != dev->driver->major || 361 ver.drm_dd_minor < 0 || 362 ver.drm_dd_minor > dev->driver->minor) 363 { 364 return EINVAL; 365 } 366 } 367 368 return 0; 369 } 370 371 /** No-op ioctl. */ 372 int drm_noop(struct drm_device *dev, void *data, 373 struct drm_file *file_priv) 374 { 375 DRM_DEBUG("\n"); 376 return 0; 377 } 378 EXPORT_SYMBOL(drm_noop); 379