2 #include <drm_fourcc.h>
6 #include "libds/util/defs.h"
7 #include "libds/interfaces/buffer.h"
8 #include "libds/linux_dmabuf_v1.h"
10 #include "pixel_format.h"
11 #include "linux-dmabuf-unstable-v1-server-protocol.h"
13 #define LINUX_DMABUF_V1_VERSION 3
15 struct ds_linux_dmabuf_v1
17 struct wl_global *global;
19 struct wl_listener display_destroy;
21 struct ds_linux_dmabuf_v1_format *supported_formats;
25 struct ds_linux_dmabuf_v1_buffer {
26 struct ds_buffer base;
27 struct wl_resource *buffer_resource;
28 struct wl_resource *params_resource;
29 struct ds_linux_dmabuf_v1_attributes attributes;
32 struct wl_listener buffer_release;
36 static const struct wl_buffer_interface linux_dmabuf_v1_buffer_impl;
39 linux_dmabuf_v1_buffer_is_instance(struct wl_resource *resource)
41 return wl_resource_instance_of(resource, &wl_buffer_interface,
42 &linux_dmabuf_v1_buffer_impl);
45 static struct ds_linux_dmabuf_v1_buffer *
46 linux_dmabuf_v1_buffer_from_resource(struct wl_resource *resource)
48 DS_ASSERT(linux_dmabuf_v1_buffer_is_instance(resource));
49 return wl_resource_get_user_data(resource);
52 static struct ds_buffer *
53 linux_dmabuf_from_resource(struct wl_resource *resource)
55 struct ds_linux_dmabuf_v1_buffer *buffer;
57 buffer = linux_dmabuf_v1_buffer_from_resource(resource);
61 static const struct ds_buffer_resource_interface
62 linux_dmabuf_resource_iface = {
63 .name = "ds_linux_dmabuf_v1",
64 .is_instance = linux_dmabuf_v1_buffer_is_instance,
65 .from_resource = linux_dmabuf_from_resource,
69 linux_dmabuf_v1_buffer_destroy(struct ds_linux_dmabuf_v1_buffer *buffer)
73 for (i = 0; i < buffer->attributes.num_planes; i++) {
74 if (buffer->attributes.fd[i] == -1) continue;
76 close(buffer->attributes.fd[i]);
83 linux_dmabuf_v1_buffer_handle_destroy(struct wl_client *client, struct wl_resource *resource)
85 wl_resource_destroy(resource);
88 static const struct wl_buffer_interface linux_dmabuf_v1_buffer_impl = {
89 linux_dmabuf_v1_buffer_handle_destroy,
93 linux_dmabuf_v1_buffer_iface_destroy(struct ds_buffer *ds_buffer)
95 struct ds_linux_dmabuf_v1_buffer *buffer;
97 buffer = wl_container_of(ds_buffer, buffer, base);
98 linux_dmabuf_v1_buffer_destroy(buffer);
101 static struct wl_resource *
102 linux_dmabuf_v1_buffer_iface_get_resource(struct ds_buffer *ds_buffer)
104 struct ds_linux_dmabuf_v1_buffer *buffer;
106 buffer = wl_container_of(ds_buffer, buffer, base);
107 return buffer->buffer_resource;
110 static const struct ds_buffer_interface linux_dmabuf_v1_buffer_iface = {
111 .destroy = linux_dmabuf_v1_buffer_iface_destroy,
112 .get_resource = linux_dmabuf_v1_buffer_iface_get_resource,
115 static void linux_dmabuf_v1_handle_destroy(struct wl_client *client,
116 struct wl_resource *resource)
118 wl_resource_destroy(resource);
122 linux_dmabuf_params_handle_resource_destroy(struct wl_resource *resource)
124 struct ds_linux_dmabuf_v1_buffer *buffer;
126 buffer = wl_resource_get_user_data(resource);
130 linux_dmabuf_v1_buffer_destroy(buffer);
134 linux_dmabuf_params_handle_destroy(struct wl_client *client,
135 struct wl_resource *params_resource)
137 wl_resource_destroy(params_resource);
141 linux_dmabuf_params_handle_add(struct wl_client *client,
142 struct wl_resource *params_resource,
147 uint32_t modifier_hi,
148 uint32_t modifier_lo)
150 struct ds_linux_dmabuf_v1_buffer *buffer;
152 buffer = wl_resource_get_user_data(params_resource);
154 wl_resource_post_error(params_resource,
155 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
156 "params was already used to create a wl_buffer");
161 if (plane_idx >= LINUX_DMABUF_MAX_PLANES) {
162 wl_resource_post_error(params_resource,
163 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX,
164 "plane index %u is too high", plane_idx);
169 if (buffer->attributes.fd[plane_idx] != -1) {
170 wl_resource_post_error(params_resource,
171 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET,
172 "a dmabuf has already been added for plane %u",
178 buffer->attributes.fd[plane_idx] = fd;
179 buffer->attributes.offset[plane_idx] = offset;
180 buffer->attributes.stride[plane_idx] = stride;
182 if (wl_resource_get_version(params_resource) < ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION)
183 buffer->attributes.modifier[plane_idx] = DRM_FORMAT_MOD_INVALID;
185 buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) | modifier_lo;
187 buffer->attributes.num_planes++;
191 linux_dmabuf_v1_buffer_handle_resource_destroy(struct wl_resource *resource)
193 struct ds_linux_dmabuf_v1_buffer *buffer;
195 buffer = wl_resource_get_user_data(resource);
196 assert(buffer->buffer_resource == resource);
197 assert(!buffer->params_resource);
199 buffer->buffer_resource = NULL;
200 ds_buffer_drop(&buffer->base);
204 linux_dmabuf_v1_buffer_handle_release(struct wl_listener *listener, void *data)
206 struct ds_linux_dmabuf_v1_buffer *buffer;
208 buffer = wl_container_of(listener, buffer, listener.buffer_release);
209 if (buffer->buffer_resource)
210 wl_buffer_send_release(buffer->buffer_resource);
214 params_create_common(struct wl_client *client,
215 struct wl_resource *params_resource,
222 struct ds_linux_dmabuf_v1_buffer *buffer;
225 buffer = wl_resource_get_user_data(params_resource);
227 wl_resource_post_error(params_resource,
228 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED,
229 "params was already used to create a wl_buffer");
233 assert(buffer->params_resource == params_resource);
234 assert(!buffer->buffer_resource);
236 /* Switch the linux_dmabuf_v1_buffer object from params resource to
237 * eventually wl_buffer resource.
239 wl_resource_set_user_data(buffer->params_resource, NULL);
240 buffer->params_resource = NULL;
242 if (!buffer->attributes.num_planes) {
243 wl_resource_post_error(params_resource,
244 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
245 "no dmabuf has been added to the params");
249 /* Check for holes in the dmabufs set (e.g. [0, 1, 3]) */
250 for (i = 0; i < buffer->attributes.num_planes; i++) {
251 if (buffer->attributes.fd[i] == -1) {
252 wl_resource_post_error(params_resource,
253 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE,
254 "no dmabuf has been added for plane %i", i);
259 buffer->attributes.width = width;
260 buffer->attributes.height = height;
261 buffer->attributes.format = format;
262 buffer->attributes.flags = flags;
264 if (width < 1 || height < 1) {
265 wl_resource_post_error(params_resource,
266 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS,
267 "invalid width %d or height %d", width, height);
271 for (i = 0; i < buffer->attributes.num_planes; i++) {
274 if ((uint64_t) buffer->attributes.offset[i] + buffer->attributes.stride[i] > UINT32_MAX) {
275 wl_resource_post_error(params_resource,
276 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
277 "size overflow for plane %i", i);
282 (uint64_t) buffer->attributes.offset[i] +
283 (uint64_t) buffer->attributes.stride[i] * height > UINT32_MAX) {
284 wl_resource_post_error(params_resource,
285 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
286 "size overflow for plane %i", i);
290 /* Don't report an error as it might be caused
291 * by the kernel not supporting seeking on dmabuf */
292 size = lseek(buffer->attributes.fd[i], 0, SEEK_END);
296 if (buffer->attributes.offset[i] >= size) {
297 wl_resource_post_error(params_resource,
298 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
299 "invalid offset %i for plane %i",
300 buffer->attributes.offset[i], i);
304 if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size) {
305 wl_resource_post_error(params_resource,
306 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
307 "invalid stride %i for plane %i",
308 buffer->attributes.stride[i], i);
312 /* Only valid for first plane as other planes might be
313 * sub-sampled according to fourcc format */
315 (uint64_t) buffer->attributes.offset[i] +
316 (uint64_t) buffer->attributes.stride[i] * height > size) {
317 wl_resource_post_error(params_resource,
318 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS,
319 "invalid buffer stride or height for plane %i", i);
324 buffer->buffer_resource = wl_resource_create(client, &wl_buffer_interface, 1, buffer_id);
325 if (!buffer->buffer_resource) {
326 wl_resource_post_no_memory(params_resource);
330 wl_resource_set_implementation(buffer->buffer_resource,
331 &linux_dmabuf_v1_buffer_impl,
332 buffer, linux_dmabuf_v1_buffer_handle_resource_destroy);
334 /* send 'created' event when the request is not for an immediate
335 * import, ie buffer_id is zero */
337 zwp_linux_buffer_params_v1_send_created(params_resource,
338 buffer->buffer_resource);
340 ds_buffer_init(&buffer->base, &linux_dmabuf_v1_buffer_iface, width, height);
342 buffer->listener.buffer_release.notify =
343 linux_dmabuf_v1_buffer_handle_release;
344 ds_buffer_add_release_listener(&buffer->base,
345 &buffer->listener.buffer_release);
351 zwp_linux_buffer_params_v1_send_failed(params_resource);
353 /* since the behavior is left implementation defined by the
354 * protocol in case of create_immed failure due to an unknown cause,
355 * we choose to treat it as a fatal error and immediately kill the
356 * client instead of creating an invalid handle and waiting for it
359 wl_resource_post_error(params_resource,
360 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
361 "importing the supplied dmabufs failed");
364 linux_dmabuf_v1_buffer_destroy(buffer);
369 linux_dmabuf_params_handle_create(struct wl_client *client,
370 struct wl_resource *params_resource,
376 params_create_common(client, params_resource, 0, width, height, format,
381 linux_dmabuf_params_handle_create_immed(struct wl_client *client,
382 struct wl_resource *params_resource,
389 params_create_common(client, params_resource, buffer_id, width, height,
393 static const struct zwp_linux_buffer_params_v1_interface linux_dmabuf_params_impl = {
394 .destroy = linux_dmabuf_params_handle_destroy,
395 .add = linux_dmabuf_params_handle_add,
396 .create = linux_dmabuf_params_handle_create,
397 .create_immed = linux_dmabuf_params_handle_create_immed,
401 linux_dmabuf_v1_handle_create_params(struct wl_client *client,
402 struct wl_resource *linux_dmabuf_resource,
405 struct ds_linux_dmabuf_v1_buffer *buffer;
409 version = wl_resource_get_version(linux_dmabuf_resource);
411 buffer = calloc(1, sizeof *buffer);
413 wl_client_post_no_memory(client);
417 buffer->params_resource = wl_resource_create(client,
418 &zwp_linux_buffer_params_v1_interface, version, id);
419 if (!buffer->params_resource) {
420 wl_client_post_no_memory(client);
425 for (i = 0; i < LINUX_DMABUF_MAX_PLANES; i++ )
426 buffer->attributes.fd[i] = -1;
428 wl_resource_set_implementation(buffer->params_resource,
429 &linux_dmabuf_params_impl, buffer,
430 linux_dmabuf_params_handle_resource_destroy);
433 static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_v1_impl = {
434 .destroy = linux_dmabuf_v1_handle_destroy,
435 .create_params = linux_dmabuf_v1_handle_create_params,
439 linux_dmabuf_v1_send_formats(struct ds_linux_dmabuf_v1 *linux_dmabuf,
440 struct wl_resource *resource)
444 for (i = 0; i < linux_dmabuf->num_formats; i++) {
445 for (j = 0; j < linux_dmabuf->supported_formats[i].num_modifiers; j++) {
446 if (wl_resource_get_version(resource) < ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
447 if ((linux_dmabuf->supported_formats[i].modifiers[j] == DRM_FORMAT_MOD_INVALID) ||
448 (linux_dmabuf->supported_formats[i].modifiers[j] == DRM_FORMAT_MOD_LINEAR))
449 zwp_linux_dmabuf_v1_send_format(resource, linux_dmabuf->supported_formats[i].format);
451 zwp_linux_dmabuf_v1_send_modifier(resource, linux_dmabuf->supported_formats[i].format,
452 linux_dmabuf->supported_formats[i].modifiers[j] >> 32,
453 linux_dmabuf->supported_formats[i].modifiers[j] & 0xFFFFFFFF);
459 static void linux_dmabuf_v1_bind(struct wl_client *client, void *data,
460 uint32_t version, uint32_t id)
462 struct wl_resource *resource;
463 struct ds_linux_dmabuf_v1 *linux_dmabuf = data;
465 resource = wl_resource_create(client,
466 &zwp_linux_dmabuf_v1_interface, version, id);
468 wl_client_post_no_memory(client);
472 wl_resource_set_implementation(resource, &linux_dmabuf_v1_impl, NULL, NULL);
474 linux_dmabuf_v1_send_formats(linux_dmabuf, resource);
478 linux_dmabuf_v1_handle_display_destroy(struct wl_listener *listener, void *data)
480 struct ds_linux_dmabuf_v1 *linux_dmabuf;
483 linux_dmabuf = wl_container_of(listener, linux_dmabuf, display_destroy);
484 wl_global_destroy(linux_dmabuf->global);
486 for (i = 0; i < linux_dmabuf->num_formats; i++) {
487 if (linux_dmabuf->supported_formats[i].modifiers)
488 free(linux_dmabuf->supported_formats[i].modifiers);
491 free(linux_dmabuf->supported_formats);
495 WL_EXPORT struct ds_linux_dmabuf_v1 *
496 ds_linux_dmabuf_v1_create(struct wl_display *display,
497 const struct ds_linux_dmabuf_v1_format *supported_formats, int num_formats)
499 struct ds_linux_dmabuf_v1 *linux_dmabuf;
502 DS_RETURN_VAL_IF_FAIL(display, NULL);
504 linux_dmabuf = calloc(1, sizeof *linux_dmabuf);
508 linux_dmabuf->supported_formats = calloc(num_formats, sizeof *supported_formats);
509 if (!linux_dmabuf->supported_formats) {
513 linux_dmabuf->num_formats = num_formats;
515 for (i = 0; i < num_formats; i++) {
516 linux_dmabuf->supported_formats[i].format = supported_formats[i].format;
518 linux_dmabuf->supported_formats[i].modifiers = calloc(linux_dmabuf->supported_formats[i].num_modifiers,
519 sizeof *linux_dmabuf->supported_formats[i].modifiers);
520 if (!linux_dmabuf->supported_formats[i].modifiers)
523 for (j = 0; j < linux_dmabuf->supported_formats[i].num_modifiers; j++)
524 linux_dmabuf->supported_formats[i].modifiers[j] = supported_formats[i].modifiers[j];
526 linux_dmabuf->supported_formats[i].num_modifiers = supported_formats[i].num_modifiers;
529 linux_dmabuf->global = wl_global_create(display,
530 &zwp_linux_dmabuf_v1_interface, LINUX_DMABUF_V1_VERSION,
531 linux_dmabuf, linux_dmabuf_v1_bind);
532 if (!linux_dmabuf->global)
535 linux_dmabuf->display_destroy.notify = linux_dmabuf_v1_handle_display_destroy;
536 wl_display_add_destroy_listener(display, &linux_dmabuf->display_destroy);
538 ds_buffer_register_resource_interface(&linux_dmabuf_resource_iface);
543 for (i = 0; i < linux_dmabuf->num_formats; i++) {
544 if (linux_dmabuf->supported_formats[i].modifiers)
545 free(linux_dmabuf->supported_formats[i].modifiers);
548 free(linux_dmabuf->supported_formats);
554 WL_EXPORT struct ds_linux_dmabuf_v1_buffer *
555 ds_linux_dmabuf_v1_buffer_from_buffer(struct ds_buffer *ds_buffer)
557 struct ds_linux_dmabuf_v1_buffer *buffer;
559 DS_RETURN_VAL_IF_FAIL(ds_buffer, NULL);
561 if (ds_buffer->iface != &linux_dmabuf_v1_buffer_iface)
564 return wl_container_of(ds_buffer, buffer, base);
567 WL_EXPORT const struct ds_linux_dmabuf_v1_attributes *
568 ds_linux_dmabuf_v1_buffer_get_attributes(struct ds_linux_dmabuf_v1_buffer *dmabuf_buffer)
570 DS_RETURN_VAL_IF_FAIL(dmabuf_buffer, NULL);
572 return &dmabuf_buffer->attributes;