+
+typedef struct _drmModeAtomicReqItem drmModeAtomicReqItem, *drmModeAtomicReqItemPtr;
+
+struct _drmModeAtomicReqItem {
+ uint32_t object_id;
+ uint32_t property_id;
+ uint64_t value;
+};
+
+struct _drmModeAtomicReq {
+ uint32_t cursor;
+ uint32_t size_items;
+ drmModeAtomicReqItemPtr items;
+};
+
+drm_public drmModeAtomicReqPtr drmModeAtomicAlloc(void)
+{
+ drmModeAtomicReqPtr req;
+
+ req = drmMalloc(sizeof *req);
+ if (!req)
+ return NULL;
+
+ req->items = NULL;
+ req->cursor = 0;
+ req->size_items = 0;
+
+ return req;
+}
+
+drm_public drmModeAtomicReqPtr drmModeAtomicDuplicate(drmModeAtomicReqPtr old)
+{
+ drmModeAtomicReqPtr new;
+
+ if (!old)
+ return NULL;
+
+ new = drmMalloc(sizeof *new);
+ if (!new)
+ return NULL;
+
+ new->cursor = old->cursor;
+ new->size_items = old->size_items;
+
+ if (old->size_items) {
+ new->items = drmMalloc(old->size_items * sizeof(*new->items));
+ if (!new->items) {
+ free(new);
+ return NULL;
+ }
+ memcpy(new->items, old->items,
+ old->cursor * sizeof(*new->items));
+ } else {
+ new->items = NULL;
+ }
+
+ return new;
+}
+
+drm_public int drmModeAtomicMerge(drmModeAtomicReqPtr base,
+ drmModeAtomicReqPtr augment)
+{
+ if (!base)
+ return -EINVAL;
+
+ if (!augment || augment->cursor == 0)
+ return 0;
+
+ if (base->cursor + augment->cursor >= base->size_items) {
+ drmModeAtomicReqItemPtr new;
+ int saved_size = base->size_items;
+
+ base->size_items = base->cursor + augment->cursor;
+ new = realloc(base->items,
+ base->size_items * sizeof(*base->items));
+ if (!new) {
+ base->size_items = saved_size;
+ return -ENOMEM;
+ }
+ base->items = new;
+ }
+
+ memcpy(&base->items[base->cursor], augment->items,
+ augment->cursor * sizeof(*augment->items));
+ base->cursor += augment->cursor;
+
+ return 0;
+}
+
+drm_public int drmModeAtomicGetCursor(drmModeAtomicReqPtr req)
+{
+ if (!req)
+ return -EINVAL;
+ return req->cursor;
+}
+
+drm_public void drmModeAtomicSetCursor(drmModeAtomicReqPtr req, int cursor)
+{
+ if (req)
+ req->cursor = cursor;
+}
+
+drm_public int drmModeAtomicAddProperty(drmModeAtomicReqPtr req,
+ uint32_t object_id,
+ uint32_t property_id,
+ uint64_t value)
+{
+ if (!req)
+ return -EINVAL;
+
+ if (object_id == 0 || property_id == 0)
+ return -EINVAL;
+
+ if (req->cursor >= req->size_items) {
+ const uint32_t item_size_inc = getpagesize() / sizeof(*req->items);
+ drmModeAtomicReqItemPtr new;
+
+ req->size_items += item_size_inc;
+ new = realloc(req->items, req->size_items * sizeof(*req->items));
+ if (!new) {
+ req->size_items -= item_size_inc;
+ return -ENOMEM;
+ }
+ req->items = new;
+ }
+
+ req->items[req->cursor].object_id = object_id;
+ req->items[req->cursor].property_id = property_id;
+ req->items[req->cursor].value = value;
+ req->cursor++;
+
+ return req->cursor;
+}
+
+drm_public void drmModeAtomicFree(drmModeAtomicReqPtr req)
+{
+ if (!req)
+ return;
+
+ if (req->items)
+ drmFree(req->items);
+ drmFree(req);
+}
+
+static int sort_req_list(const void *misc, const void *other)
+{
+ const drmModeAtomicReqItem *first = misc;
+ const drmModeAtomicReqItem *second = other;
+
+ if (first->object_id < second->object_id)
+ return -1;
+ else if (first->object_id > second->object_id)
+ return 1;
+ else
+ return second->property_id - first->property_id;
+}
+
+drm_public int drmModeAtomicCommit(int fd, drmModeAtomicReqPtr req,
+ uint32_t flags, void *user_data)
+{
+ drmModeAtomicReqPtr sorted;
+ struct drm_mode_atomic atomic;
+ uint32_t *objs_ptr = NULL;
+ uint32_t *count_props_ptr = NULL;
+ uint32_t *props_ptr = NULL;
+ uint64_t *prop_values_ptr = NULL;
+ uint32_t last_obj_id = 0;
+ uint32_t i;
+ int obj_idx = -1;
+ int ret = -1;
+
+ if (!req)
+ return -EINVAL;
+
+ if (req->cursor == 0)
+ return 0;
+
+ sorted = drmModeAtomicDuplicate(req);
+ if (sorted == NULL)
+ return -ENOMEM;
+
+ memclear(atomic);
+
+ /* Sort the list by object ID, then by property ID. */
+ qsort(sorted->items, sorted->cursor, sizeof(*sorted->items),
+ sort_req_list);
+
+ /* Now the list is sorted, eliminate duplicate property sets. */
+ for (i = 0; i < sorted->cursor; i++) {
+ if (sorted->items[i].object_id != last_obj_id) {
+ atomic.count_objs++;
+ last_obj_id = sorted->items[i].object_id;
+ }
+
+ if (i == sorted->cursor - 1)
+ continue;
+
+ if (sorted->items[i].object_id != sorted->items[i + 1].object_id ||
+ sorted->items[i].property_id != sorted->items[i + 1].property_id)
+ continue;
+
+ memmove(&sorted->items[i], &sorted->items[i + 1],
+ (sorted->cursor - i - 1) * sizeof(*sorted->items));
+ sorted->cursor--;
+ }
+
+ objs_ptr = drmMalloc(atomic.count_objs * sizeof objs_ptr[0]);
+ if (!objs_ptr) {
+ errno = ENOMEM;
+ goto out;
+ }
+
+ count_props_ptr = drmMalloc(atomic.count_objs * sizeof count_props_ptr[0]);
+ if (!count_props_ptr) {
+ errno = ENOMEM;
+ goto out;
+ }
+
+ props_ptr = drmMalloc(sorted->cursor * sizeof props_ptr[0]);
+ if (!props_ptr) {
+ errno = ENOMEM;
+ goto out;
+ }
+
+ prop_values_ptr = drmMalloc(sorted->cursor * sizeof prop_values_ptr[0]);
+ if (!prop_values_ptr) {
+ errno = ENOMEM;
+ goto out;
+ }
+
+ for (i = 0, last_obj_id = 0; i < sorted->cursor; i++) {
+ if (sorted->items[i].object_id != last_obj_id) {
+ obj_idx++;
+ objs_ptr[obj_idx] = sorted->items[i].object_id;
+ last_obj_id = objs_ptr[obj_idx];
+ }
+
+ count_props_ptr[obj_idx]++;
+ props_ptr[i] = sorted->items[i].property_id;
+ prop_values_ptr[i] = sorted->items[i].value;
+
+ }
+
+ atomic.flags = flags;
+ atomic.objs_ptr = VOID2U64(objs_ptr);
+ atomic.count_props_ptr = VOID2U64(count_props_ptr);
+ atomic.props_ptr = VOID2U64(props_ptr);
+ atomic.prop_values_ptr = VOID2U64(prop_values_ptr);
+ atomic.user_data = VOID2U64(user_data);
+
+ ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_ATOMIC, &atomic);
+
+out:
+ drmFree(objs_ptr);
+ drmFree(count_props_ptr);
+ drmFree(props_ptr);
+ drmFree(prop_values_ptr);
+ drmModeAtomicFree(sorted);
+
+ return ret;
+}
+
+drm_public int
+drmModeCreatePropertyBlob(int fd, const void *data, size_t length,
+ uint32_t *id)
+{
+ struct drm_mode_create_blob create;
+ int ret;
+
+ if (length >= 0xffffffff)
+ return -ERANGE;
+
+ memclear(create);
+
+ create.length = length;
+ create.data = (uintptr_t) data;
+ create.blob_id = 0;
+ *id = 0;
+
+ ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_CREATEPROPBLOB, &create);
+ if (ret != 0)
+ return ret;
+
+ *id = create.blob_id;
+ return 0;
+}
+
+drm_public int
+drmModeDestroyPropertyBlob(int fd, uint32_t id)
+{
+ struct drm_mode_destroy_blob destroy;
+
+ memclear(destroy);
+ destroy.blob_id = id;
+ return DRM_IOCTL(fd, DRM_IOCTL_MODE_DESTROYPROPBLOB, &destroy);
+}
+
+drm_public int
+drmModeCreateLease(int fd, const uint32_t *objects, int num_objects, int flags,
+ uint32_t *lessee_id)
+{
+ struct drm_mode_create_lease create;
+ int ret;
+
+ memclear(create);
+ create.object_ids = (uintptr_t) objects;
+ create.object_count = num_objects;
+ create.flags = flags;
+
+ ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_CREATE_LEASE, &create);
+ if (ret == 0) {
+ *lessee_id = create.lessee_id;
+ return create.fd;
+ }
+ return -errno;
+}
+
+drm_public drmModeLesseeListPtr
+drmModeListLessees(int fd)
+{
+ struct drm_mode_list_lessees list;
+ uint32_t count;
+ drmModeLesseeListPtr ret;
+
+ memclear(list);
+
+ if (DRM_IOCTL(fd, DRM_IOCTL_MODE_LIST_LESSEES, &list))
+ return NULL;
+
+ count = list.count_lessees;
+ ret = drmMalloc(sizeof (drmModeLesseeListRes) + count * sizeof (ret->lessees[0]));
+ if (!ret)
+ return NULL;
+
+ list.lessees_ptr = VOID2U64(&ret->lessees[0]);
+ if (DRM_IOCTL(fd, DRM_IOCTL_MODE_LIST_LESSEES, &list)) {
+ drmFree(ret);
+ return NULL;
+ }
+
+ ret->count = count;
+ return ret;
+}
+
+drm_public drmModeObjectListPtr
+drmModeGetLease(int fd)
+{
+ struct drm_mode_get_lease get;
+ uint32_t count;
+ drmModeObjectListPtr ret;
+
+ memclear(get);
+
+ if (DRM_IOCTL(fd, DRM_IOCTL_MODE_GET_LEASE, &get))
+ return NULL;
+
+ count = get.count_objects;
+ ret = drmMalloc(sizeof (drmModeObjectListRes) + count * sizeof (ret->objects[0]));
+ if (!ret)
+ return NULL;
+
+ get.objects_ptr = VOID2U64(&ret->objects[0]);
+ if (DRM_IOCTL(fd, DRM_IOCTL_MODE_GET_LEASE, &get)) {
+ drmFree(ret);
+ return NULL;
+ }
+
+ ret->count = count;
+ return ret;
+}
+
+drm_public int
+drmModeRevokeLease(int fd, uint32_t lessee_id)
+{
+ struct drm_mode_revoke_lease revoke;
+ int ret;
+
+ memclear(revoke);
+
+ revoke.lessee_id = lessee_id;
+
+ ret = DRM_IOCTL(fd, DRM_IOCTL_MODE_REVOKE_LEASE, &revoke);
+ if (ret == 0)
+ return 0;
+ return -errno;
+}
+
+drm_public drmModeFB2Ptr
+drmModeGetFB2(int fd, uint32_t fb_id)
+{
+ struct drm_mode_fb_cmd2 get = {
+ .fb_id = fb_id,
+ };
+ drmModeFB2Ptr ret;
+ int err;
+
+ err = DRM_IOCTL(fd, DRM_IOCTL_MODE_GETFB2, &get);
+ if (err != 0)
+ return NULL;
+
+ ret = drmMalloc(sizeof(drmModeFB2));
+ if (!ret)
+ return NULL;
+
+ ret->fb_id = fb_id;
+ ret->width = get.width;
+ ret->height = get.height;
+ ret->pixel_format = get.pixel_format;
+ ret->flags = get.flags;
+ ret->modifier = get.modifier[0];
+ memcpy(ret->handles, get.handles, sizeof(uint32_t) * 4);
+ memcpy(ret->pitches, get.pitches, sizeof(uint32_t) * 4);
+ memcpy(ret->offsets, get.offsets, sizeof(uint32_t) * 4);
+
+ return ret;
+}
+
+drm_public void drmModeFreeFB2(drmModeFB2Ptr ptr)
+{
+ drmFree(ptr);
+}