Home | History | Annotate | Download | only in libdrm
      1 /*
      2  * \file xf86drmMode.c
      3  * Header for DRM modesetting interface.
      4  *
      5  * \author Jakob Bornecrantz <wallbraker (at) gmail.com>
      6  *
      7  * \par Acknowledgements:
      8  * Feb 2007, Dave Airlie <airlied (at) linux.ie>
      9  */
     10 
     11 /*
     12  * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, Texas.
     13  * Copyright (c) 2007-2008 Dave Airlie <airlied (at) linux.ie>
     14  * Copyright (c) 2007-2008 Jakob Bornecrantz <wallbraker (at) gmail.com>
     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 shall be included in
     24  * all copies or substantial portions of the Software.
     25  *
     26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     27  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     28  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     29  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     30  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     31  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
     32  * IN THE SOFTWARE.
     33  *
     34  */
     35 
     36 /*
     37  * TODO the types we are after are defined in different headers on different
     38  * platforms find which headers to include to get uint32_t
     39  */
     40 
     41 #ifdef HAVE_CONFIG_H
     42 #include "config.h"
     43 #endif
     44 
     45 #include <limits.h>
     46 #include <stdint.h>
     47 #include <stdlib.h>
     48 #include <sys/ioctl.h>
     49 #ifdef HAVE_SYS_SYSCTL_H
     50 #include <sys/sysctl.h>
     51 #endif
     52 #include <stdio.h>
     53 #include <stdbool.h>
     54 
     55 #include "xf86drmMode.h"
     56 #include "xf86drm.h"
     57 #include <drm.h>
     58 #include <string.h>
     59 #include <dirent.h>
     60 #include <unistd.h>
     61 #include <errno.h>
     62 
     63 #define memclear(s) memset(&s, 0, sizeof(s))
     64 
     65 #define U642VOID(x) ((void *)(unsigned long)(x))
     66 #define VOID2U64(x) ((uint64_t)(unsigned long)(x))
     67 
     68 static inline int DRM_IOCTL(int fd, unsigned long cmd, void *arg)
     69 {
     70 	int ret = drmIoctl(fd, cmd, arg);
     71 	return ret < 0 ? -errno : ret;
     72 }
     73 
     74 /*
     75  * Util functions
     76  */
     77 
     78 static void* drmAllocCpy(char *array, int count, int entry_size)
     79 {
     80 	char *r;
     81 	int i;
     82 
     83 	if (!count || !array || !entry_size)
     84 		return 0;
     85 
     86 	if (!(r = drmMalloc(count*entry_size)))
     87 		return 0;
     88 
     89 	for (i = 0; i < count; i++)
     90 		memcpy(r+(entry_size*i), array+(entry_size*i), entry_size);
     91 
     92 	return r;
     93 }
     94 
     95 /*
     96  * A couple of free functions.
     97  */
     98 
     99 void drmModeFreeModeInfo(drmModeModeInfoPtr ptr)
    100 {
    101 	if (!ptr)
    102 		return;
    103 
    104 	drmFree(ptr);
    105 }
    106 
    107 void drmModeFreeResources(drmModeResPtr ptr)
    108 {
    109 	if (!ptr)
    110 		return;
    111 
    112 	drmFree(ptr->fbs);
    113 	drmFree(ptr->crtcs);
    114 	drmFree(ptr->connectors);
    115 	drmFree(ptr->encoders);
    116 	drmFree(ptr);
    117 }
    118 
    119 void drmModeFreeFB(drmModeFBPtr ptr)
    120 {
    121 	if (!ptr)
    122 		return;
    123 
    124 	/* we might add more frees later. */
    125 	drmFree(ptr);
    126 }
    127 
    128 void drmModeFreeCrtc(drmModeCrtcPtr ptr)
    129 {
    130 	if (!ptr)
    131 		return;
    132 
    133 	drmFree(ptr);
    134 }
    135 
    136 void drmModeFreeConnector(drmModeConnectorPtr ptr)
    137 {
    138 	if (!ptr)
    139 		return;
    140 
    141 	drmFree(ptr->encoders);
    142 	drmFree(ptr->prop_values);
    143 	drmFree(ptr->props);
    144 	drmFree(ptr->modes);
    145 	drmFree(ptr);
    146 }
    147 
    148 void drmModeFreeEncoder(drmModeEncoderPtr ptr)
    149 {
    150 	drmFree(ptr);
    151 }
    152 
    153 /*
    154  * ModeSetting functions.
    155  */
    156 
    157 drmModeResPtr drmModeGetResources(int fd)
    158 {
    159 	struct drm_mode_card_res res, counts;
    160 	drmModeResPtr r = 0;
    161 
    162 retry:
    163 	memclear(res);
    164 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
    165 		return 0;
    166 
    167 	counts = res;
    168 
    169 	if (res.count_fbs) {
    170 		res.fb_id_ptr = VOID2U64(drmMalloc(res.count_fbs*sizeof(uint32_t)));
    171 		if (!res.fb_id_ptr)
    172 			goto err_allocs;
    173 	}
    174 	if (res.count_crtcs) {
    175 		res.crtc_id_ptr = VOID2U64(drmMalloc(res.count_crtcs*sizeof(uint32_t)));
    176 		if (!res.crtc_id_ptr)
    177 			goto err_allocs;
    178 	}
    179 	if (res.count_connectors) {
    180 		res.connector_id_ptr = VOID2U64(drmMalloc(res.count_connectors*sizeof(uint32_t)));
    181 		if (!res.connector_id_ptr)
    182 			goto err_allocs;
    183 	}
    184 	if (res.count_encoders) {
    185 		res.encoder_id_ptr = VOID2U64(drmMalloc(res.count_encoders*sizeof(uint32_t)));
    186 		if (!res.encoder_id_ptr)
    187 			goto err_allocs;
    188 	}
    189 
    190 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
    191 		goto err_allocs;
    192 
    193 	/* The number of available connectors and etc may have changed with a
    194 	 * hotplug event in between the ioctls, in which case the field is
    195 	 * silently ignored by the kernel.
    196 	 */
    197 	if (counts.count_fbs < res.count_fbs ||
    198 	    counts.count_crtcs < res.count_crtcs ||
    199 	    counts.count_connectors < res.count_connectors ||
    200 	    counts.count_encoders < res.count_encoders)
    201 	{
    202 		drmFree(U642VOID(res.fb_id_ptr));
    203 		drmFree(U642VOID(res.crtc_id_ptr));
    204 		drmFree(U642VOID(res.connector_id_ptr));
    205 		drmFree(U642VOID(res.encoder_id_ptr));
    206 
    207 		goto retry;
    208 	}
    209 
    210 	/*
    211 	 * return
    212 	 */
    213 	if (!(r = drmMalloc(sizeof(*r))))
    214 		goto err_allocs;
    215 
    216 	r->min_width     = res.min_width;
    217 	r->max_width     = res.max_width;
    218 	r->min_height    = res.min_height;
    219 	r->max_height    = res.max_height;
    220 	r->count_fbs     = res.count_fbs;
    221 	r->count_crtcs   = res.count_crtcs;
    222 	r->count_connectors = res.count_connectors;
    223 	r->count_encoders = res.count_encoders;
    224 
    225 	r->fbs        = drmAllocCpy(U642VOID(res.fb_id_ptr), res.count_fbs, sizeof(uint32_t));
    226 	r->crtcs      = drmAllocCpy(U642VOID(res.crtc_id_ptr), res.count_crtcs, sizeof(uint32_t));
    227 	r->connectors = drmAllocCpy(U642VOID(res.connector_id_ptr), res.count_connectors, sizeof(uint32_t));
    228 	r->encoders   = drmAllocCpy(U642VOID(res.encoder_id_ptr), res.count_encoders, sizeof(uint32_t));
    229 	if ((res.count_fbs && !r->fbs) ||
    230 	    (res.count_crtcs && !r->crtcs) ||
    231 	    (res.count_connectors && !r->connectors) ||
    232 	    (res.count_encoders && !r->encoders))
    233 	{
    234 		drmFree(r->fbs);
    235 		drmFree(r->crtcs);
    236 		drmFree(r->connectors);
    237 		drmFree(r->encoders);
    238 		drmFree(r);
    239 		r = 0;
    240 	}
    241 
    242 err_allocs:
    243 	drmFree(U642VOID(res.fb_id_ptr));
    244 	drmFree(U642VOID(res.crtc_id_ptr));
    245 	drmFree(U642VOID(res.connector_id_ptr));
    246 	drmFree(U642VOID(res.encoder_id_ptr));
    247 
    248 	return r;
    249 }
    250 
    251 int drmModeAddFB(int fd, uint32_t width, uint32_t height, uint8_t depth,
    252 		 uint8_t bpp, uint32_t pitch, uint32_t bo_handle,
    253 		 uint32_t *buf_id)
    254 {
    255 	struct drm_mode_fb_cmd f;
    256 	int ret;
    257 
    258 	memclear(f);
    259 	f.width  = width;
    260 	f.height = height;
    261 	f.pitch  = pitch;
    262 	f.bpp    = bpp;
    263 	f.depth  = depth;
    264 	f.handle = bo_handle;
    265 
    266 	if ((ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ADDFB, &f)))
    267 		return ret;
    268 
    269 	*buf_id = f.fb_id;
    270 	return 0;
    271 }
    272 
    273 int drmModeAddFB2WithModifiers(int fd, uint32_t width, uint32_t height,
    274                                uint32_t pixel_format, uint32_t bo_handles[4],
    275                                uint32_t pitches[4], uint32_t offsets[4],
    276                                uint64_t modifier[4], uint32_t *buf_id, uint32_t flags)
    277 {
    278 	struct drm_mode_fb_cmd2 f;
    279 	int ret;
    280 
    281 	memclear(f);
    282 	f.width  = width;
    283 	f.height = height;
    284 	f.pixel_format = pixel_format;
    285 	f.flags = flags;
    286 	memcpy(f.handles, bo_handles, 4 * sizeof(bo_handles[0]));
    287 	memcpy(f.pitches, pitches, 4 * sizeof(pitches[0]));
    288 	memcpy(f.offsets, offsets, 4 * sizeof(offsets[0]));
    289 	if (modifier)
    290 		memcpy(f.modifier, modifier, 4 * sizeof(modifier[0]));
    291 
    292 	if ((ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ADDFB2, &f)))
    293 		return ret;
    294 
    295 	*buf_id = f.fb_id;
    296 	return 0;
    297 }
    298 
    299 int drmModeAddFB2(int fd, uint32_t width, uint32_t height,
    300                   uint32_t pixel_format, uint32_t bo_handles[4],
    301                   uint32_t pitches[4], uint32_t offsets[4],
    302                   uint32_t *buf_id, uint32_t flags)
    303 {
    304 	return drmModeAddFB2WithModifiers(fd, width, height,
    305 					  pixel_format, bo_handles,
    306 					  pitches, offsets, NULL,
    307 					  buf_id, flags);
    308 }
    309 
    310 int drmModeRmFB(int fd, uint32_t bufferId)
    311 {
    312 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_RMFB, &bufferId);
    313 }
    314 
    315 drmModeFBPtr drmModeGetFB(int fd, uint32_t buf)
    316 {
    317 	struct drm_mode_fb_cmd info;
    318 	drmModeFBPtr r;
    319 
    320 	memclear(info);
    321 	info.fb_id = buf;
    322 
    323 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETFB, &info))
    324 		return NULL;
    325 
    326 	if (!(r = drmMalloc(sizeof(*r))))
    327 		return NULL;
    328 
    329 	r->fb_id = info.fb_id;
    330 	r->width = info.width;
    331 	r->height = info.height;
    332 	r->pitch = info.pitch;
    333 	r->bpp = info.bpp;
    334 	r->handle = info.handle;
    335 	r->depth = info.depth;
    336 
    337 	return r;
    338 }
    339 
    340 int drmModeDirtyFB(int fd, uint32_t bufferId,
    341 		   drmModeClipPtr clips, uint32_t num_clips)
    342 {
    343 	struct drm_mode_fb_dirty_cmd dirty;
    344 
    345 	memclear(dirty);
    346 	dirty.fb_id = bufferId;
    347 	dirty.clips_ptr = VOID2U64(clips);
    348 	dirty.num_clips = num_clips;
    349 
    350 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_DIRTYFB, &dirty);
    351 }
    352 
    353 /*
    354  * Crtc functions
    355  */
    356 
    357 drmModeCrtcPtr drmModeGetCrtc(int fd, uint32_t crtcId)
    358 {
    359 	struct drm_mode_crtc crtc;
    360 	drmModeCrtcPtr r;
    361 
    362 	memclear(crtc);
    363 	crtc.crtc_id = crtcId;
    364 
    365 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETCRTC, &crtc))
    366 		return 0;
    367 
    368 	/*
    369 	 * return
    370 	 */
    371 
    372 	if (!(r = drmMalloc(sizeof(*r))))
    373 		return 0;
    374 
    375 	r->crtc_id         = crtc.crtc_id;
    376 	r->x               = crtc.x;
    377 	r->y               = crtc.y;
    378 	r->mode_valid      = crtc.mode_valid;
    379 	if (r->mode_valid) {
    380 		memcpy(&r->mode, &crtc.mode, sizeof(struct drm_mode_modeinfo));
    381 		r->width = crtc.mode.hdisplay;
    382 		r->height = crtc.mode.vdisplay;
    383 	}
    384 	r->buffer_id       = crtc.fb_id;
    385 	r->gamma_size      = crtc.gamma_size;
    386 	return r;
    387 }
    388 
    389 int drmModeSetCrtc(int fd, uint32_t crtcId, uint32_t bufferId,
    390 		   uint32_t x, uint32_t y, uint32_t *connectors, int count,
    391 		   drmModeModeInfoPtr mode)
    392 {
    393 	struct drm_mode_crtc crtc;
    394 
    395 	memclear(crtc);
    396 	crtc.x             = x;
    397 	crtc.y             = y;
    398 	crtc.crtc_id       = crtcId;
    399 	crtc.fb_id         = bufferId;
    400 	crtc.set_connectors_ptr = VOID2U64(connectors);
    401 	crtc.count_connectors = count;
    402 	if (mode) {
    403 	  memcpy(&crtc.mode, mode, sizeof(struct drm_mode_modeinfo));
    404 	  crtc.mode_valid = 1;
    405 	}
    406 
    407 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETCRTC, &crtc);
    408 }
    409 
    410 /*
    411  * Cursor manipulation
    412  */
    413 
    414 int drmModeSetCursor(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height)
    415 {
    416 	struct drm_mode_cursor arg;
    417 
    418 	memclear(arg);
    419 	arg.flags = DRM_MODE_CURSOR_BO;
    420 	arg.crtc_id = crtcId;
    421 	arg.width = width;
    422 	arg.height = height;
    423 	arg.handle = bo_handle;
    424 
    425 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_CURSOR, &arg);
    426 }
    427 
    428 int drmModeSetCursor2(int fd, uint32_t crtcId, uint32_t bo_handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y)
    429 {
    430 	struct drm_mode_cursor2 arg;
    431 
    432 	memclear(arg);
    433 	arg.flags = DRM_MODE_CURSOR_BO;
    434 	arg.crtc_id = crtcId;
    435 	arg.width = width;
    436 	arg.height = height;
    437 	arg.handle = bo_handle;
    438 	arg.hot_x = hot_x;
    439 	arg.hot_y = hot_y;
    440 
    441 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_CURSOR2, &arg);
    442 }
    443 
    444 int drmModeMoveCursor(int fd, uint32_t crtcId, int x, int y)
    445 {
    446 	struct drm_mode_cursor arg;
    447 
    448 	memclear(arg);
    449 	arg.flags = DRM_MODE_CURSOR_MOVE;
    450 	arg.crtc_id = crtcId;
    451 	arg.x = x;
    452 	arg.y = y;
    453 
    454 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_CURSOR, &arg);
    455 }
    456 
    457 /*
    458  * Encoder get
    459  */
    460 drmModeEncoderPtr drmModeGetEncoder(int fd, uint32_t encoder_id)
    461 {
    462 	struct drm_mode_get_encoder enc;
    463 	drmModeEncoderPtr r = NULL;
    464 
    465 	memclear(enc);
    466 	enc.encoder_id = encoder_id;
    467 
    468 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETENCODER, &enc))
    469 		return 0;
    470 
    471 	if (!(r = drmMalloc(sizeof(*r))))
    472 		return 0;
    473 
    474 	r->encoder_id = enc.encoder_id;
    475 	r->crtc_id = enc.crtc_id;
    476 	r->encoder_type = enc.encoder_type;
    477 	r->possible_crtcs = enc.possible_crtcs;
    478 	r->possible_clones = enc.possible_clones;
    479 
    480 	return r;
    481 }
    482 
    483 /*
    484  * Connector manipulation
    485  */
    486 static drmModeConnectorPtr
    487 _drmModeGetConnector(int fd, uint32_t connector_id, int probe)
    488 {
    489 	struct drm_mode_get_connector conn, counts;
    490 	drmModeConnectorPtr r = NULL;
    491 	struct drm_mode_modeinfo stack_mode;
    492 
    493 	memclear(conn);
    494 	conn.connector_id = connector_id;
    495 	if (!probe) {
    496 		conn.count_modes = 1;
    497 		conn.modes_ptr = VOID2U64(&stack_mode);
    498 	}
    499 
    500 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
    501 		return 0;
    502 
    503 retry:
    504 	counts = conn;
    505 
    506 	if (conn.count_props) {
    507 		conn.props_ptr = VOID2U64(drmMalloc(conn.count_props*sizeof(uint32_t)));
    508 		if (!conn.props_ptr)
    509 			goto err_allocs;
    510 		conn.prop_values_ptr = VOID2U64(drmMalloc(conn.count_props*sizeof(uint64_t)));
    511 		if (!conn.prop_values_ptr)
    512 			goto err_allocs;
    513 	}
    514 
    515 	if (conn.count_modes) {
    516 		conn.modes_ptr = VOID2U64(drmMalloc(conn.count_modes*sizeof(struct drm_mode_modeinfo)));
    517 		if (!conn.modes_ptr)
    518 			goto err_allocs;
    519 	} else {
    520 		conn.count_modes = 1;
    521 		conn.modes_ptr = VOID2U64(&stack_mode);
    522 	}
    523 
    524 	if (conn.count_encoders) {
    525 		conn.encoders_ptr = VOID2U64(drmMalloc(conn.count_encoders*sizeof(uint32_t)));
    526 		if (!conn.encoders_ptr)
    527 			goto err_allocs;
    528 	}
    529 
    530 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
    531 		goto err_allocs;
    532 
    533 	/* The number of available connectors and etc may have changed with a
    534 	 * hotplug event in between the ioctls, in which case the field is
    535 	 * silently ignored by the kernel.
    536 	 */
    537 	if (counts.count_props < conn.count_props ||
    538 	    counts.count_modes < conn.count_modes ||
    539 	    counts.count_encoders < conn.count_encoders) {
    540 		drmFree(U642VOID(conn.props_ptr));
    541 		drmFree(U642VOID(conn.prop_values_ptr));
    542 		if (U642VOID(conn.modes_ptr) != &stack_mode)
    543 			drmFree(U642VOID(conn.modes_ptr));
    544 		drmFree(U642VOID(conn.encoders_ptr));
    545 
    546 		goto retry;
    547 	}
    548 
    549 	if(!(r = drmMalloc(sizeof(*r)))) {
    550 		goto err_allocs;
    551 	}
    552 
    553 	r->connector_id = conn.connector_id;
    554 	r->encoder_id = conn.encoder_id;
    555 	r->connection   = conn.connection;
    556 	r->mmWidth      = conn.mm_width;
    557 	r->mmHeight     = conn.mm_height;
    558 	/* convert subpixel from kernel to userspace */
    559 	r->subpixel     = conn.subpixel + 1;
    560 	r->count_modes  = conn.count_modes;
    561 	r->count_props  = conn.count_props;
    562 	r->props        = drmAllocCpy(U642VOID(conn.props_ptr), conn.count_props, sizeof(uint32_t));
    563 	r->prop_values  = drmAllocCpy(U642VOID(conn.prop_values_ptr), conn.count_props, sizeof(uint64_t));
    564 	r->modes        = drmAllocCpy(U642VOID(conn.modes_ptr), conn.count_modes, sizeof(struct drm_mode_modeinfo));
    565 	r->count_encoders = conn.count_encoders;
    566 	r->encoders     = drmAllocCpy(U642VOID(conn.encoders_ptr), conn.count_encoders, sizeof(uint32_t));
    567 	r->connector_type  = conn.connector_type;
    568 	r->connector_type_id = conn.connector_type_id;
    569 
    570 	if ((r->count_props && !r->props) ||
    571 	    (r->count_props && !r->prop_values) ||
    572 	    (r->count_modes && !r->modes) ||
    573 	    (r->count_encoders && !r->encoders)) {
    574 		drmFree(r->props);
    575 		drmFree(r->prop_values);
    576 		drmFree(r->modes);
    577 		drmFree(r->encoders);
    578 		drmFree(r);
    579 		r = 0;
    580 	}
    581 
    582 err_allocs:
    583 	drmFree(U642VOID(conn.prop_values_ptr));
    584 	drmFree(U642VOID(conn.props_ptr));
    585 	if (U642VOID(conn.modes_ptr) != &stack_mode)
    586 		drmFree(U642VOID(conn.modes_ptr));
    587 	drmFree(U642VOID(conn.encoders_ptr));
    588 
    589 	return r;
    590 }
    591 
    592 drmModeConnectorPtr drmModeGetConnector(int fd, uint32_t connector_id)
    593 {
    594 	return _drmModeGetConnector(fd, connector_id, 1);
    595 }
    596 
    597 drmModeConnectorPtr drmModeGetConnectorCurrent(int fd, uint32_t connector_id)
    598 {
    599 	return _drmModeGetConnector(fd, connector_id, 0);
    600 }
    601 
    602 int drmModeAttachMode(int fd, uint32_t connector_id, drmModeModeInfoPtr mode_info)
    603 {
    604 	struct drm_mode_mode_cmd res;
    605 
    606 	memclear(res);
    607 	memcpy(&res.mode, mode_info, sizeof(struct drm_mode_modeinfo));
    608 	res.connector_id = connector_id;
    609 
    610 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_ATTACHMODE, &res);
    611 }
    612 
    613 int drmModeDetachMode(int fd, uint32_t connector_id, drmModeModeInfoPtr mode_info)
    614 {
    615 	struct drm_mode_mode_cmd res;
    616 
    617 	memclear(res);
    618 	memcpy(&res.mode, mode_info, sizeof(struct drm_mode_modeinfo));
    619 	res.connector_id = connector_id;
    620 
    621 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_DETACHMODE, &res);
    622 }
    623 
    624 drmModePropertyPtr drmModeGetProperty(int fd, uint32_t property_id)
    625 {
    626 	struct drm_mode_get_property prop;
    627 	drmModePropertyPtr r;
    628 
    629 	memclear(prop);
    630 	prop.prop_id = property_id;
    631 
    632 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPROPERTY, &prop))
    633 		return 0;
    634 
    635 	if (prop.count_values)
    636 		prop.values_ptr = VOID2U64(drmMalloc(prop.count_values * sizeof(uint64_t)));
    637 
    638 	if (prop.count_enum_blobs && (prop.flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)))
    639 		prop.enum_blob_ptr = VOID2U64(drmMalloc(prop.count_enum_blobs * sizeof(struct drm_mode_property_enum)));
    640 
    641 	if (prop.count_enum_blobs && (prop.flags & DRM_MODE_PROP_BLOB)) {
    642 		prop.values_ptr = VOID2U64(drmMalloc(prop.count_enum_blobs * sizeof(uint32_t)));
    643 		prop.enum_blob_ptr = VOID2U64(drmMalloc(prop.count_enum_blobs * sizeof(uint32_t)));
    644 	}
    645 
    646 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPROPERTY, &prop)) {
    647 		r = NULL;
    648 		goto err_allocs;
    649 	}
    650 
    651 	if (!(r = drmMalloc(sizeof(*r))))
    652 		return NULL;
    653 
    654 	r->prop_id = prop.prop_id;
    655 	r->count_values = prop.count_values;
    656 
    657 	r->flags = prop.flags;
    658 	if (prop.count_values)
    659 		r->values = drmAllocCpy(U642VOID(prop.values_ptr), prop.count_values, sizeof(uint64_t));
    660 	if (prop.flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) {
    661 		r->count_enums = prop.count_enum_blobs;
    662 		r->enums = drmAllocCpy(U642VOID(prop.enum_blob_ptr), prop.count_enum_blobs, sizeof(struct drm_mode_property_enum));
    663 	} else if (prop.flags & DRM_MODE_PROP_BLOB) {
    664 		r->values = drmAllocCpy(U642VOID(prop.values_ptr), prop.count_enum_blobs, sizeof(uint32_t));
    665 		r->blob_ids = drmAllocCpy(U642VOID(prop.enum_blob_ptr), prop.count_enum_blobs, sizeof(uint32_t));
    666 		r->count_blobs = prop.count_enum_blobs;
    667 	}
    668 	strncpy(r->name, prop.name, DRM_PROP_NAME_LEN);
    669 	r->name[DRM_PROP_NAME_LEN-1] = 0;
    670 
    671 err_allocs:
    672 	drmFree(U642VOID(prop.values_ptr));
    673 	drmFree(U642VOID(prop.enum_blob_ptr));
    674 
    675 	return r;
    676 }
    677 
    678 void drmModeFreeProperty(drmModePropertyPtr ptr)
    679 {
    680 	if (!ptr)
    681 		return;
    682 
    683 	drmFree(ptr->values);
    684 	drmFree(ptr->enums);
    685 	drmFree(ptr);
    686 }
    687 
    688 drmModePropertyBlobPtr drmModeGetPropertyBlob(int fd, uint32_t blob_id)
    689 {
    690 	struct drm_mode_get_blob blob;
    691 	drmModePropertyBlobPtr r;
    692 
    693 	memclear(blob);
    694 	blob.blob_id = blob_id;
    695 
    696 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob))
    697 		return NULL;
    698 
    699 	if (blob.length)
    700 		blob.data = VOID2U64(drmMalloc(blob.length));
    701 
    702 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPROPBLOB, &blob)) {
    703 		r = NULL;
    704 		goto err_allocs;
    705 	}
    706 
    707 	if (!(r = drmMalloc(sizeof(*r))))
    708 		goto err_allocs;
    709 
    710 	r->id = blob.blob_id;
    711 	r->length = blob.length;
    712 	r->data = drmAllocCpy(U642VOID(blob.data), 1, blob.length);
    713 
    714 err_allocs:
    715 	drmFree(U642VOID(blob.data));
    716 	return r;
    717 }
    718 
    719 void drmModeFreePropertyBlob(drmModePropertyBlobPtr ptr)
    720 {
    721 	if (!ptr)
    722 		return;
    723 
    724 	drmFree(ptr->data);
    725 	drmFree(ptr);
    726 }
    727 
    728 int drmModeConnectorSetProperty(int fd, uint32_t connector_id, uint32_t property_id,
    729 			     uint64_t value)
    730 {
    731 	struct drm_mode_connector_set_property osp;
    732 
    733 	memclear(osp);
    734 	osp.connector_id = connector_id;
    735 	osp.prop_id = property_id;
    736 	osp.value = value;
    737 
    738 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETPROPERTY, &osp);
    739 }
    740 
    741 /*
    742  * checks if a modesetting capable driver has attached to the pci id
    743  * returns 0 if modesetting supported.
    744  *  -EINVAL or invalid bus id
    745  *  -ENOSYS if no modesetting support
    746 */
    747 int drmCheckModesettingSupported(const char *busid)
    748 {
    749 #if defined (__linux__)
    750 	char pci_dev_dir[1024];
    751 	int domain, bus, dev, func;
    752 	DIR *sysdir;
    753 	struct dirent *dent;
    754 	int found = 0, ret;
    755 
    756 	ret = sscanf(busid, "pci:%04x:%02x:%02x.%d", &domain, &bus, &dev, &func);
    757 	if (ret != 4)
    758 		return -EINVAL;
    759 
    760 	sprintf(pci_dev_dir, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/drm",
    761 		domain, bus, dev, func);
    762 
    763 	sysdir = opendir(pci_dev_dir);
    764 	if (sysdir) {
    765 		dent = readdir(sysdir);
    766 		while (dent) {
    767 			if (!strncmp(dent->d_name, "controlD", 8)) {
    768 				found = 1;
    769 				break;
    770 			}
    771 
    772 			dent = readdir(sysdir);
    773 		}
    774 		closedir(sysdir);
    775 		if (found)
    776 			return 0;
    777 	}
    778 
    779 	sprintf(pci_dev_dir, "/sys/bus/pci/devices/%04x:%02x:%02x.%d/",
    780 		domain, bus, dev, func);
    781 
    782 	sysdir = opendir(pci_dev_dir);
    783 	if (!sysdir)
    784 		return -EINVAL;
    785 
    786 	dent = readdir(sysdir);
    787 	while (dent) {
    788 		if (!strncmp(dent->d_name, "drm:controlD", 12)) {
    789 			found = 1;
    790 			break;
    791 		}
    792 
    793 		dent = readdir(sysdir);
    794 	}
    795 
    796 	closedir(sysdir);
    797 	if (found)
    798 		return 0;
    799 #elif defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
    800 	char kbusid[1024], sbusid[1024];
    801 	char oid[128];
    802 	int domain, bus, dev, func;
    803 	int i, modesetting, ret;
    804 	size_t len;
    805 
    806 	ret = sscanf(busid, "pci:%04x:%02x:%02x.%d", &domain, &bus, &dev,
    807 	    &func);
    808 	if (ret != 4)
    809 		return -EINVAL;
    810 	snprintf(kbusid, sizeof(kbusid), "pci:%04x:%02x:%02x.%d", domain, bus,
    811 	    dev, func);
    812 
    813 	/* How many GPUs do we expect in the machine ? */
    814 	for (i = 0; i < 16; i++) {
    815 		snprintf(oid, sizeof(oid), "hw.dri.%d.busid", i);
    816 		len = sizeof(sbusid);
    817 		ret = sysctlbyname(oid, sbusid, &len, NULL, 0);
    818 		if (ret == -1) {
    819 			if (errno == ENOENT)
    820 				continue;
    821 			return -EINVAL;
    822 		}
    823 		if (strcmp(sbusid, kbusid) != 0)
    824 			continue;
    825 		snprintf(oid, sizeof(oid), "hw.dri.%d.modesetting", i);
    826 		len = sizeof(modesetting);
    827 		ret = sysctlbyname(oid, &modesetting, &len, NULL, 0);
    828 		if (ret == -1 || len != sizeof(modesetting))
    829 			return -EINVAL;
    830 		return (modesetting ? 0 : -ENOSYS);
    831 	}
    832 #elif defined(__DragonFly__)
    833 	return 0;
    834 #endif
    835 #ifdef __OpenBSD__
    836 	int	fd;
    837 	struct drm_mode_card_res res;
    838 	drmModeResPtr r = 0;
    839 
    840 	if ((fd = drmOpen(NULL, busid)) < 0)
    841 		return -EINVAL;
    842 
    843 	memset(&res, 0, sizeof(struct drm_mode_card_res));
    844 
    845 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res)) {
    846 		drmClose(fd);
    847 		return -errno;
    848 	}
    849 
    850 	drmClose(fd);
    851 	return 0;
    852 #endif
    853 	return -ENOSYS;
    854 }
    855 
    856 int drmModeCrtcGetGamma(int fd, uint32_t crtc_id, uint32_t size,
    857 			uint16_t *red, uint16_t *green, uint16_t *blue)
    858 {
    859 	struct drm_mode_crtc_lut l;
    860 
    861 	memclear(l);
    862 	l.crtc_id = crtc_id;
    863 	l.gamma_size = size;
    864 	l.red = VOID2U64(red);
    865 	l.green = VOID2U64(green);
    866 	l.blue = VOID2U64(blue);
    867 
    868 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_GETGAMMA, &l);
    869 }
    870 
    871 int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size,
    872 			uint16_t *red, uint16_t *green, uint16_t *blue)
    873 {
    874 	struct drm_mode_crtc_lut l;
    875 
    876 	memclear(l);
    877 	l.crtc_id = crtc_id;
    878 	l.gamma_size = size;
    879 	l.red = VOID2U64(red);
    880 	l.green = VOID2U64(green);
    881 	l.blue = VOID2U64(blue);
    882 
    883 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETGAMMA, &l);
    884 }
    885 
    886 int drmHandleEvent(int fd, drmEventContextPtr evctx)
    887 {
    888 	char buffer[1024];
    889 	int len, i;
    890 	struct drm_event *e;
    891 	struct drm_event_vblank *vblank;
    892 
    893 	/* The DRM read semantics guarantees that we always get only
    894 	 * complete events. */
    895 
    896 	len = read(fd, buffer, sizeof buffer);
    897 	if (len == 0)
    898 		return 0;
    899 	if (len < (int)sizeof *e)
    900 		return -1;
    901 
    902 	i = 0;
    903 	while (i < len) {
    904 		e = (struct drm_event *)(buffer + i);
    905 		switch (e->type) {
    906 		case DRM_EVENT_VBLANK:
    907 			if (evctx->version < 1 ||
    908 			    evctx->vblank_handler == NULL)
    909 				break;
    910 			vblank = (struct drm_event_vblank *) e;
    911 			evctx->vblank_handler(fd,
    912 					      vblank->sequence,
    913 					      vblank->tv_sec,
    914 					      vblank->tv_usec,
    915 					      U642VOID (vblank->user_data));
    916 			break;
    917 		case DRM_EVENT_FLIP_COMPLETE:
    918 			if (evctx->version < 2 ||
    919 			    evctx->page_flip_handler == NULL)
    920 				break;
    921 			vblank = (struct drm_event_vblank *) e;
    922 			evctx->page_flip_handler(fd,
    923 						 vblank->sequence,
    924 						 vblank->tv_sec,
    925 						 vblank->tv_usec,
    926 						 U642VOID (vblank->user_data));
    927 			break;
    928 		default:
    929 			break;
    930 		}
    931 		i += e->length;
    932 	}
    933 
    934 	return 0;
    935 }
    936 
    937 int drmModePageFlip(int fd, uint32_t crtc_id, uint32_t fb_id,
    938 		    uint32_t flags, void *user_data)
    939 {
    940 	struct drm_mode_crtc_page_flip flip;
    941 
    942 	memclear(flip);
    943 	flip.fb_id = fb_id;
    944 	flip.crtc_id = crtc_id;
    945 	flip.user_data = VOID2U64(user_data);
    946 	flip.flags = flags;
    947 
    948 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_PAGE_FLIP, &flip);
    949 }
    950 
    951 int drmModePageFlipTarget(int fd, uint32_t crtc_id, uint32_t fb_id,
    952 			  uint32_t flags, void *user_data,
    953 			  uint32_t target_vblank)
    954 {
    955 	struct drm_mode_crtc_page_flip_target flip_target;
    956 
    957 	memclear(flip_target);
    958 	flip_target.fb_id = fb_id;
    959 	flip_target.crtc_id = crtc_id;
    960 	flip_target.user_data = VOID2U64(user_data);
    961 	flip_target.flags = flags;
    962 	flip_target.sequence = target_vblank;
    963 
    964 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_PAGE_FLIP, &flip_target);
    965 }
    966 
    967 int drmModeSetPlane(int fd, uint32_t plane_id, uint32_t crtc_id,
    968 		    uint32_t fb_id, uint32_t flags,
    969 		    int32_t crtc_x, int32_t crtc_y,
    970 		    uint32_t crtc_w, uint32_t crtc_h,
    971 		    uint32_t src_x, uint32_t src_y,
    972 		    uint32_t src_w, uint32_t src_h)
    973 {
    974 	struct drm_mode_set_plane s;
    975 
    976 	memclear(s);
    977 	s.plane_id = plane_id;
    978 	s.crtc_id = crtc_id;
    979 	s.fb_id = fb_id;
    980 	s.flags = flags;
    981 	s.crtc_x = crtc_x;
    982 	s.crtc_y = crtc_y;
    983 	s.crtc_w = crtc_w;
    984 	s.crtc_h = crtc_h;
    985 	s.src_x = src_x;
    986 	s.src_y = src_y;
    987 	s.src_w = src_w;
    988 	s.src_h = src_h;
    989 
    990 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETPLANE, &s);
    991 }
    992 
    993 drmModePlanePtr drmModeGetPlane(int fd, uint32_t plane_id)
    994 {
    995 	struct drm_mode_get_plane ovr, counts;
    996 	drmModePlanePtr r = 0;
    997 
    998 retry:
    999 	memclear(ovr);
   1000 	ovr.plane_id = plane_id;
   1001 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPLANE, &ovr))
   1002 		return 0;
   1003 
   1004 	counts = ovr;
   1005 
   1006 	if (ovr.count_format_types) {
   1007 		ovr.format_type_ptr = VOID2U64(drmMalloc(ovr.count_format_types *
   1008 							 sizeof(uint32_t)));
   1009 		if (!ovr.format_type_ptr)
   1010 			goto err_allocs;
   1011 	}
   1012 
   1013 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPLANE, &ovr))
   1014 		goto err_allocs;
   1015 
   1016 	if (counts.count_format_types < ovr.count_format_types) {
   1017 		drmFree(U642VOID(ovr.format_type_ptr));
   1018 		goto retry;
   1019 	}
   1020 
   1021 	if (!(r = drmMalloc(sizeof(*r))))
   1022 		goto err_allocs;
   1023 
   1024 	r->count_formats = ovr.count_format_types;
   1025 	r->plane_id = ovr.plane_id;
   1026 	r->crtc_id = ovr.crtc_id;
   1027 	r->fb_id = ovr.fb_id;
   1028 	r->possible_crtcs = ovr.possible_crtcs;
   1029 	r->gamma_size = ovr.gamma_size;
   1030 	r->formats = drmAllocCpy(U642VOID(ovr.format_type_ptr),
   1031 				 ovr.count_format_types, sizeof(uint32_t));
   1032 	if (ovr.count_format_types && !r->formats) {
   1033 		drmFree(r->formats);
   1034 		drmFree(r);
   1035 		r = 0;
   1036 	}
   1037 
   1038 err_allocs:
   1039 	drmFree(U642VOID(ovr.format_type_ptr));
   1040 
   1041 	return r;
   1042 }
   1043 
   1044 void drmModeFreePlane(drmModePlanePtr ptr)
   1045 {
   1046 	if (!ptr)
   1047 		return;
   1048 
   1049 	drmFree(ptr->formats);
   1050 	drmFree(ptr);
   1051 }
   1052 
   1053 drmModePlaneResPtr drmModeGetPlaneResources(int fd)
   1054 {
   1055 	struct drm_mode_get_plane_res res, counts;
   1056 	drmModePlaneResPtr r = 0;
   1057 
   1058 retry:
   1059 	memclear(res);
   1060 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPLANERESOURCES, &res))
   1061 		return 0;
   1062 
   1063 	counts = res;
   1064 
   1065 	if (res.count_planes) {
   1066 		res.plane_id_ptr = VOID2U64(drmMalloc(res.count_planes *
   1067 							sizeof(uint32_t)));
   1068 		if (!res.plane_id_ptr)
   1069 			goto err_allocs;
   1070 	}
   1071 
   1072 	if (drmIoctl(fd, DRM_IOCTL_MODE_GETPLANERESOURCES, &res))
   1073 		goto err_allocs;
   1074 
   1075 	if (counts.count_planes < res.count_planes) {
   1076 		drmFree(U642VOID(res.plane_id_ptr));
   1077 		goto retry;
   1078 	}
   1079 
   1080 	if (!(r = drmMalloc(sizeof(*r))))
   1081 		goto err_allocs;
   1082 
   1083 	r->count_planes = res.count_planes;
   1084 	r->planes = drmAllocCpy(U642VOID(res.plane_id_ptr),
   1085 				  res.count_planes, sizeof(uint32_t));
   1086 	if (res.count_planes && !r->planes) {
   1087 		drmFree(r->planes);
   1088 		drmFree(r);
   1089 		r = 0;
   1090 	}
   1091 
   1092 err_allocs:
   1093 	drmFree(U642VOID(res.plane_id_ptr));
   1094 
   1095 	return r;
   1096 }
   1097 
   1098 void drmModeFreePlaneResources(drmModePlaneResPtr ptr)
   1099 {
   1100 	if (!ptr)
   1101 		return;
   1102 
   1103 	drmFree(ptr->planes);
   1104 	drmFree(ptr);
   1105 }
   1106 
   1107 drmModeObjectPropertiesPtr drmModeObjectGetProperties(int fd,
   1108 						      uint32_t object_id,
   1109 						      uint32_t object_type)
   1110 {
   1111 	struct drm_mode_obj_get_properties properties;
   1112 	drmModeObjectPropertiesPtr ret = NULL;
   1113 	uint32_t count;
   1114 
   1115 retry:
   1116 	memclear(properties);
   1117 	properties.obj_id = object_id;
   1118 	properties.obj_type = object_type;
   1119 
   1120 	if (drmIoctl(fd, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, &properties))
   1121 		return 0;
   1122 
   1123 	count = properties.count_props;
   1124 
   1125 	if (count) {
   1126 		properties.props_ptr = VOID2U64(drmMalloc(count *
   1127 							  sizeof(uint32_t)));
   1128 		if (!properties.props_ptr)
   1129 			goto err_allocs;
   1130 		properties.prop_values_ptr = VOID2U64(drmMalloc(count *
   1131 						      sizeof(uint64_t)));
   1132 		if (!properties.prop_values_ptr)
   1133 			goto err_allocs;
   1134 	}
   1135 
   1136 	if (drmIoctl(fd, DRM_IOCTL_MODE_OBJ_GETPROPERTIES, &properties))
   1137 		goto err_allocs;
   1138 
   1139 	if (count < properties.count_props) {
   1140 		drmFree(U642VOID(properties.props_ptr));
   1141 		drmFree(U642VOID(properties.prop_values_ptr));
   1142 		goto retry;
   1143 	}
   1144 	count = properties.count_props;
   1145 
   1146 	ret = drmMalloc(sizeof(*ret));
   1147 	if (!ret)
   1148 		goto err_allocs;
   1149 
   1150 	ret->count_props = count;
   1151 	ret->props = drmAllocCpy(U642VOID(properties.props_ptr),
   1152 				 count, sizeof(uint32_t));
   1153 	ret->prop_values = drmAllocCpy(U642VOID(properties.prop_values_ptr),
   1154 				       count, sizeof(uint64_t));
   1155 	if (ret->count_props && (!ret->props || !ret->prop_values)) {
   1156 		drmFree(ret->props);
   1157 		drmFree(ret->prop_values);
   1158 		drmFree(ret);
   1159 		ret = NULL;
   1160 	}
   1161 
   1162 err_allocs:
   1163 	drmFree(U642VOID(properties.props_ptr));
   1164 	drmFree(U642VOID(properties.prop_values_ptr));
   1165 	return ret;
   1166 }
   1167 
   1168 void drmModeFreeObjectProperties(drmModeObjectPropertiesPtr ptr)
   1169 {
   1170 	if (!ptr)
   1171 		return;
   1172 	drmFree(ptr->props);
   1173 	drmFree(ptr->prop_values);
   1174 	drmFree(ptr);
   1175 }
   1176 
   1177 int drmModeObjectSetProperty(int fd, uint32_t object_id, uint32_t object_type,
   1178 			     uint32_t property_id, uint64_t value)
   1179 {
   1180 	struct drm_mode_obj_set_property prop;
   1181 
   1182 	memclear(prop);
   1183 	prop.value = value;
   1184 	prop.prop_id = property_id;
   1185 	prop.obj_id = object_id;
   1186 	prop.obj_type = object_type;
   1187 
   1188 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_OBJ_SETPROPERTY, &prop);
   1189 }
   1190 
   1191 typedef struct _drmModePropertySetItem drmModePropertySetItem, *drmModePropertySetItemPtr;
   1192 
   1193 struct _drmModePropertySetItem {
   1194 	uint32_t object_id;
   1195 	uint32_t property_id;
   1196 	bool is_blob;
   1197 	uint64_t value;
   1198 	void *blob;
   1199 	drmModePropertySetItemPtr next;
   1200 };
   1201 
   1202 struct _drmModePropertySet {
   1203 	unsigned int count_objs;
   1204 	unsigned int count_props;
   1205 	unsigned int count_blobs;
   1206 	drmModePropertySetItem list;
   1207 };
   1208 
   1209 drmModePropertySetPtr drmModePropertySetAlloc(void)
   1210 {
   1211 	drmModePropertySetPtr set;
   1212 
   1213 	set = drmMalloc(sizeof *set);
   1214 	if (!set)
   1215 		return NULL;
   1216 
   1217 	set->list.next = NULL;
   1218 	set->count_props = 0;
   1219 	set->count_objs = 0;
   1220 
   1221 	return set;
   1222 }
   1223 
   1224 int drmModePropertySetAdd(drmModePropertySetPtr set,
   1225 			  uint32_t object_id,
   1226 			  uint32_t property_id,
   1227 			  uint64_t value)
   1228 {
   1229 	drmModePropertySetItemPtr prev = &set->list;
   1230 	bool new_obj = false;
   1231 
   1232 	/* keep it sorted by object_id and property_id */
   1233 	while (prev->next) {
   1234 		if (prev->next->object_id > object_id)
   1235 			break;
   1236 
   1237 		if (prev->next->object_id == object_id &&
   1238 		    prev->next->property_id >= property_id)
   1239 			break;
   1240 
   1241 		prev = prev->next;
   1242 	}
   1243 
   1244 	if ((prev == &set->list || prev->object_id != object_id) &&
   1245 	    (!prev->next || prev->next->object_id != object_id))
   1246 		new_obj = true;
   1247 
   1248 	/* replace or add? */
   1249 	if (prev->next &&
   1250 	    prev->next->object_id == object_id &&
   1251 	    prev->next->property_id == property_id) {
   1252 		drmModePropertySetItemPtr item = prev->next;
   1253 
   1254 		if (item->is_blob)
   1255 			return -EINVAL;
   1256 
   1257 		item->value = value;
   1258 	} else {
   1259 		drmModePropertySetItemPtr item;
   1260 
   1261 		item = drmMalloc(sizeof *item);
   1262 		if (!item)
   1263 			return -1;
   1264 
   1265 		item->object_id = object_id;
   1266 		item->property_id = property_id;
   1267 		item->value = value;
   1268 		item->is_blob = false;
   1269 		item->blob = NULL;
   1270 
   1271 		item->next = prev->next;
   1272 		prev->next = item;
   1273 
   1274 		set->count_props++;
   1275 	}
   1276 
   1277 	if (new_obj)
   1278 		set->count_objs++;
   1279 
   1280 	return 0;
   1281 }
   1282 
   1283 int drmModePropertySetAddBlob(drmModePropertySetPtr set,
   1284 			      uint32_t object_id,
   1285 			      uint32_t property_id,
   1286 			      uint64_t length,
   1287 			      void *data)
   1288 {
   1289 	drmModePropertySetItemPtr prev = &set->list;
   1290 	bool new_obj = false;
   1291 
   1292 	/* keep it sorted by object_id and property_id */
   1293 	while (prev->next) {
   1294 		if (prev->next->object_id > object_id)
   1295 			break;
   1296 
   1297 		if (prev->next->object_id == object_id &&
   1298 		    prev->next->property_id >= property_id)
   1299 			break;
   1300 
   1301 		prev = prev->next;
   1302 	}
   1303 
   1304 	if ((prev == &set->list || prev->object_id != object_id) &&
   1305 	    (!prev->next || prev->next->object_id != object_id))
   1306 		new_obj = true;
   1307 
   1308 	/* replace or add? */
   1309 	if (prev->next &&
   1310 	    prev->next->object_id == object_id &&
   1311 	    prev->next->property_id == property_id) {
   1312 		drmModePropertySetItemPtr item = prev->next;
   1313 
   1314 		if (!item->is_blob)
   1315 			return -EINVAL;
   1316 
   1317 		item->value = length;
   1318 		item->blob = data;
   1319 	} else {
   1320 		drmModePropertySetItemPtr item;
   1321 
   1322 		item = drmMalloc(sizeof *item);
   1323 		if (!item)
   1324 			return -1;
   1325 
   1326 		item->object_id = object_id;
   1327 		item->property_id = property_id;
   1328 		item->is_blob = true;
   1329 		item->value = length;
   1330 		item->blob = data;
   1331 
   1332 		item->next = prev->next;
   1333 		prev->next = item;
   1334 
   1335 		set->count_props++;
   1336 		set->count_blobs++;
   1337 	}
   1338 
   1339 	if (new_obj)
   1340 		set->count_objs++;
   1341 
   1342 	return 0;
   1343 }
   1344 
   1345 void drmModePropertySetFree(drmModePropertySetPtr set)
   1346 {
   1347 	drmModePropertySetItemPtr item;
   1348 
   1349 	if (!set)
   1350 		return;
   1351 
   1352 	item = set->list.next;
   1353 
   1354 	while (item) {
   1355 		drmModePropertySetItemPtr next = item->next;
   1356 
   1357 		drmFree(item);
   1358 
   1359 		item = next;
   1360 	}
   1361 
   1362 	drmFree(set);
   1363 }
   1364 
   1365 int drmModePropertySetCommit(int fd, uint32_t flags, void *user_data,
   1366 			     drmModePropertySetPtr set)
   1367 {
   1368 	drmModePropertySetItemPtr item;
   1369 	uint32_t *objs_ptr = NULL;
   1370 	uint32_t *count_props_ptr = NULL;
   1371 	uint32_t *props_ptr = NULL;
   1372 	uint64_t *prop_values_ptr = NULL;
   1373 	uint64_t *blob_values_ptr = NULL;
   1374 	struct drm_mode_atomic atomic = { 0 };
   1375 	unsigned int obj_idx = 0;
   1376 	unsigned int prop_idx = 0;
   1377 	unsigned int blob_idx = 0;
   1378 	int ret = -1;
   1379 
   1380 	if (!set)
   1381 		return -1;
   1382 
   1383 	objs_ptr = drmMalloc(set->count_objs * sizeof objs_ptr[0]);
   1384 	if (!objs_ptr) {
   1385 		errno = ENOMEM;
   1386 		goto out;
   1387 	}
   1388 
   1389 	count_props_ptr = drmMalloc(set->count_objs * sizeof count_props_ptr[0]);
   1390 	if (!count_props_ptr) {
   1391 		errno = ENOMEM;
   1392 		goto out;
   1393 	}
   1394 
   1395 	props_ptr = drmMalloc(set->count_props * sizeof props_ptr[0]);
   1396 	if (!props_ptr) {
   1397 		errno = ENOMEM;
   1398 		goto out;
   1399 	}
   1400 
   1401 	prop_values_ptr = drmMalloc(set->count_props * sizeof prop_values_ptr[0]);
   1402 	if (!prop_values_ptr) {
   1403 		errno = ENOMEM;
   1404 		goto out;
   1405 	}
   1406 
   1407 	blob_values_ptr = drmMalloc(set->count_blobs * sizeof blob_values_ptr[0]);
   1408 	if (!blob_values_ptr) {
   1409 		errno = ENOMEM;
   1410 		goto out;
   1411 	}
   1412 
   1413 	item = set->list.next;
   1414 
   1415 	while (item) {
   1416 		int count_props = 0;
   1417 		drmModePropertySetItemPtr next = item;
   1418 
   1419 		objs_ptr[obj_idx] = item->object_id;
   1420 
   1421 		while (next && next->object_id == item->object_id) {
   1422 			props_ptr[prop_idx] = next->property_id;
   1423 			prop_values_ptr[prop_idx] = next->value;
   1424 			prop_idx++;
   1425 
   1426 			if (next->is_blob)
   1427 				blob_values_ptr[blob_idx++] = VOID2U64(next->blob);
   1428 
   1429 			count_props++;
   1430 
   1431 			next = next->next;
   1432 		}
   1433 
   1434 		count_props_ptr[obj_idx++] = count_props;
   1435 
   1436 		item = next;
   1437 	}
   1438 
   1439 	atomic.count_objs = set->count_objs;
   1440 	atomic.flags = flags;
   1441 	atomic.objs_ptr = VOID2U64(objs_ptr);
   1442 	atomic.count_props_ptr = VOID2U64(count_props_ptr);
   1443 	atomic.props_ptr = VOID2U64(props_ptr);
   1444 	atomic.prop_values_ptr = VOID2U64(prop_values_ptr);
   1445 // TODO:
   1446 //	atomic.blob_values_ptr = VOID2U64(blob_values_ptr);
   1447 	atomic.user_data = VOID2U64(user_data);
   1448 
   1449 	ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ATOMIC, &atomic);
   1450 
   1451 out:
   1452 	drmFree(objs_ptr);
   1453 	drmFree(count_props_ptr);
   1454 	drmFree(props_ptr);
   1455 	drmFree(prop_values_ptr);
   1456 
   1457 	return ret;
   1458 }
   1459 
   1460 typedef struct _drmModeAtomicReqItem drmModeAtomicReqItem, *drmModeAtomicReqItemPtr;
   1461 
   1462 struct _drmModeAtomicReqItem {
   1463 	uint32_t object_id;
   1464 	uint32_t property_id;
   1465 	uint64_t value;
   1466 };
   1467 
   1468 struct _drmModeAtomicReq {
   1469 	uint32_t cursor;
   1470 	uint32_t size_items;
   1471 	drmModeAtomicReqItemPtr items;
   1472 };
   1473 
   1474 drmModeAtomicReqPtr drmModeAtomicAlloc(void)
   1475 {
   1476 	drmModeAtomicReqPtr req;
   1477 
   1478 	req = drmMalloc(sizeof *req);
   1479 	if (!req)
   1480 		return NULL;
   1481 
   1482 	req->items = NULL;
   1483 	req->cursor = 0;
   1484 	req->size_items = 0;
   1485 
   1486 	return req;
   1487 }
   1488 
   1489 drmModeAtomicReqPtr drmModeAtomicDuplicate(drmModeAtomicReqPtr old)
   1490 {
   1491 	drmModeAtomicReqPtr new;
   1492 
   1493 	if (!old)
   1494 		return NULL;
   1495 
   1496 	new = drmMalloc(sizeof *new);
   1497 	if (!new)
   1498 		return NULL;
   1499 
   1500 	new->cursor = old->cursor;
   1501 	new->size_items = old->size_items;
   1502 
   1503 	if (old->size_items) {
   1504 		new->items = drmMalloc(old->size_items * sizeof(*new->items));
   1505 		if (!new->items) {
   1506 			free(new);
   1507 			return NULL;
   1508 		}
   1509 		memcpy(new->items, old->items,
   1510 		       old->size_items * sizeof(*new->items));
   1511 	} else {
   1512 		new->items = NULL;
   1513 	}
   1514 
   1515 	return new;
   1516 }
   1517 
   1518 int drmModeAtomicMerge(drmModeAtomicReqPtr base, drmModeAtomicReqPtr augment)
   1519 {
   1520 	if (!base)
   1521 		return -EINVAL;
   1522 
   1523 	if (!augment || augment->cursor == 0)
   1524 		return 0;
   1525 
   1526 	if (base->cursor + augment->cursor >= base->size_items) {
   1527 		drmModeAtomicReqItemPtr new;
   1528 		int saved_size = base->size_items;
   1529 
   1530 		base->size_items = base->cursor + augment->cursor;
   1531 		new = realloc(base->items,
   1532 			      base->size_items * sizeof(*base->items));
   1533 		if (!new) {
   1534 			base->size_items = saved_size;
   1535 			return -ENOMEM;
   1536 		}
   1537 		base->items = new;
   1538 	}
   1539 
   1540 	memcpy(&base->items[base->cursor], augment->items,
   1541 	       augment->cursor * sizeof(*augment->items));
   1542 	base->cursor += augment->cursor;
   1543 
   1544 	return 0;
   1545 }
   1546 
   1547 int drmModeAtomicGetCursor(drmModeAtomicReqPtr req)
   1548 {
   1549 	if (!req)
   1550 		return -EINVAL;
   1551 	return req->cursor;
   1552 }
   1553 
   1554 void drmModeAtomicSetCursor(drmModeAtomicReqPtr req, int cursor)
   1555 {
   1556 	if (req)
   1557 		req->cursor = cursor;
   1558 }
   1559 
   1560 int drmModeAtomicAddProperty(drmModeAtomicReqPtr req,
   1561 			     uint32_t object_id,
   1562 			     uint32_t property_id,
   1563 			     uint64_t value)
   1564 {
   1565 	if (!req)
   1566 		return -EINVAL;
   1567 
   1568 	if (req->cursor >= req->size_items) {
   1569 		drmModeAtomicReqItemPtr new;
   1570 
   1571 		req->size_items += 16;
   1572 		new = realloc(req->items, req->size_items * sizeof(*req->items));
   1573 		if (!new) {
   1574 			req->size_items -= 16;
   1575 			return -ENOMEM;
   1576 		}
   1577 		req->items = new;
   1578 	}
   1579 
   1580 	req->items[req->cursor].object_id = object_id;
   1581 	req->items[req->cursor].property_id = property_id;
   1582 	req->items[req->cursor].value = value;
   1583 	req->cursor++;
   1584 
   1585 	return req->cursor;
   1586 }
   1587 
   1588 void drmModeAtomicFree(drmModeAtomicReqPtr req)
   1589 {
   1590 	if (!req)
   1591 		return;
   1592 
   1593 	if (req->items)
   1594 		drmFree(req->items);
   1595 	drmFree(req);
   1596 }
   1597 
   1598 static int sort_req_list(const void *misc, const void *other)
   1599 {
   1600 	const drmModeAtomicReqItem *first = misc;
   1601 	const drmModeAtomicReqItem *second = other;
   1602 
   1603 	if (first->object_id < second->object_id)
   1604 		return -1;
   1605 	else if (first->object_id > second->object_id)
   1606 		return 1;
   1607 	else
   1608 		return second->property_id - first->property_id;
   1609 }
   1610 
   1611 int drmModeAtomicCommit(int fd, drmModeAtomicReqPtr req, uint32_t flags,
   1612 			void *user_data)
   1613 {
   1614 	drmModeAtomicReqPtr sorted;
   1615 	struct drm_mode_atomic atomic;
   1616 	uint32_t *objs_ptr = NULL;
   1617 	uint32_t *count_props_ptr = NULL;
   1618 	uint32_t *props_ptr = NULL;
   1619 	uint64_t *prop_values_ptr = NULL;
   1620 	uint32_t last_obj_id = 0;
   1621 	uint32_t i;
   1622 	int obj_idx = -1;
   1623 	int ret = -1;
   1624 
   1625 	if (!req)
   1626 		return -EINVAL;
   1627 
   1628 	if (req->cursor == 0)
   1629 		return 0;
   1630 
   1631 	sorted = drmModeAtomicDuplicate(req);
   1632 	if (sorted == NULL)
   1633 		return -ENOMEM;
   1634 
   1635 	memclear(atomic);
   1636 
   1637 	/* Sort the list by object ID, then by property ID. */
   1638 	qsort(sorted->items, sorted->cursor, sizeof(*sorted->items),
   1639 	      sort_req_list);
   1640 
   1641 	/* Now the list is sorted, eliminate duplicate property sets. */
   1642 	for (i = 0; i < sorted->cursor; i++) {
   1643 		if (sorted->items[i].object_id != last_obj_id) {
   1644 			atomic.count_objs++;
   1645 			last_obj_id = sorted->items[i].object_id;
   1646 		}
   1647 
   1648 		if (i == sorted->cursor - 1)
   1649 			continue;
   1650 
   1651 		if (sorted->items[i].object_id != sorted->items[i + 1].object_id ||
   1652 		    sorted->items[i].property_id != sorted->items[i + 1].property_id)
   1653 			continue;
   1654 
   1655 		memmove(&sorted->items[i], &sorted->items[i + 1],
   1656 			(sorted->cursor - i - 1) * sizeof(*sorted->items));
   1657 		sorted->cursor--;
   1658 	}
   1659 
   1660 	objs_ptr = drmMalloc(atomic.count_objs * sizeof objs_ptr[0]);
   1661 	if (!objs_ptr) {
   1662 		errno = ENOMEM;
   1663 		goto out;
   1664 	}
   1665 
   1666 	count_props_ptr = drmMalloc(atomic.count_objs * sizeof count_props_ptr[0]);
   1667 	if (!count_props_ptr) {
   1668 		errno = ENOMEM;
   1669 		goto out;
   1670 	}
   1671 
   1672 	props_ptr = drmMalloc(sorted->cursor * sizeof props_ptr[0]);
   1673 	if (!props_ptr) {
   1674 		errno = ENOMEM;
   1675 		goto out;
   1676 	}
   1677 
   1678 	prop_values_ptr = drmMalloc(sorted->cursor * sizeof prop_values_ptr[0]);
   1679 	if (!prop_values_ptr) {
   1680 		errno = ENOMEM;
   1681 		goto out;
   1682 	}
   1683 
   1684 	for (i = 0, last_obj_id = 0; i < sorted->cursor; i++) {
   1685 		if (sorted->items[i].object_id != last_obj_id) {
   1686 			obj_idx++;
   1687 			objs_ptr[obj_idx] = sorted->items[i].object_id;
   1688 			last_obj_id = objs_ptr[obj_idx];
   1689 		}
   1690 
   1691 		count_props_ptr[obj_idx]++;
   1692 		props_ptr[i] = sorted->items[i].property_id;
   1693 		prop_values_ptr[i] = sorted->items[i].value;
   1694 
   1695 	}
   1696 
   1697 	atomic.flags = flags;
   1698 	atomic.objs_ptr = VOID2U64(objs_ptr);
   1699 	atomic.count_props_ptr = VOID2U64(count_props_ptr);
   1700 	atomic.props_ptr = VOID2U64(props_ptr);
   1701 	atomic.prop_values_ptr = VOID2U64(prop_values_ptr);
   1702 	atomic.user_data = VOID2U64(user_data);
   1703 
   1704 	ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ATOMIC, &atomic);
   1705 
   1706 out:
   1707 	drmFree(objs_ptr);
   1708 	drmFree(count_props_ptr);
   1709 	drmFree(props_ptr);
   1710 	drmFree(prop_values_ptr);
   1711 	drmModeAtomicFree(sorted);
   1712 
   1713 	return ret;
   1714 }
   1715 
   1716 int
   1717 drmModeCreatePropertyBlob(int fd, const void *data, size_t length, uint32_t *id)
   1718 {
   1719 	struct drm_mode_create_blob create;
   1720 	int ret;
   1721 
   1722 	if (length >= 0xffffffff)
   1723 		return -ERANGE;
   1724 
   1725 	memclear(create);
   1726 
   1727 	create.length = length;
   1728 	create.data = (uintptr_t) data;
   1729 	create.blob_id = 0;
   1730 	*id = 0;
   1731 
   1732 	ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_CREATEPROPBLOB, &create);
   1733 	if (ret != 0)
   1734 		return ret;
   1735 
   1736 	*id = create.blob_id;
   1737 	return 0;
   1738 }
   1739 
   1740 int
   1741 drmModeDestroyPropertyBlob(int fd, uint32_t id)
   1742 {
   1743 	struct drm_mode_destroy_blob destroy;
   1744 
   1745 	memclear(destroy);
   1746 	destroy.blob_id = id;
   1747 	return DRM_IOCTL(fd, DRM_IOCTL_MODE_DESTROYPROPBLOB, &destroy);
   1748 }
   1749