add ds_linux_dmabuf_v1
[platform/core/uifw/libds.git] / src / client_buffer / linux_dmabuf_v1.c
1 #include <stdlib.h>
2 #include <drm_fourcc.h>
3 #include <assert.h>
4
5 #include "libds/log.h"
6 #include "libds/util/defs.h"
7 #include "libds/interfaces/buffer.h"
8 #include "libds/linux_dmabuf_v1.h"
9
10 #include "pixel_format.h"
11 #include "linux-dmabuf-unstable-v1-server-protocol.h"
12
13 #define LINUX_DMABUF_V1_VERSION 3
14
15 struct ds_linux_dmabuf_v1
16 {
17     struct wl_global *global;
18
19     struct wl_listener display_destroy;
20
21     struct ds_linux_dmabuf_v1_format *supported_formats;
22     int num_formats;
23 };
24
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;
30
31     struct {
32         struct wl_listener buffer_release;
33     } listener;
34 };
35
36 static const struct wl_buffer_interface linux_dmabuf_v1_buffer_impl;                                                        
37
38 static bool
39 linux_dmabuf_v1_buffer_is_instance(struct wl_resource *resource)
40 {
41     return wl_resource_instance_of(resource, &wl_buffer_interface,
42             &linux_dmabuf_v1_buffer_impl);
43 }
44
45 static struct ds_linux_dmabuf_v1_buffer *
46 linux_dmabuf_v1_buffer_from_resource(struct wl_resource *resource)
47 {
48     DS_ASSERT(linux_dmabuf_v1_buffer_is_instance(resource));
49     return wl_resource_get_user_data(resource);
50 }
51
52 static struct ds_buffer *
53 linux_dmabuf_from_resource(struct wl_resource *resource)
54 {
55     struct ds_linux_dmabuf_v1_buffer *buffer;
56
57     buffer = linux_dmabuf_v1_buffer_from_resource(resource);
58     return &buffer->base;
59 }
60
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,
66 };
67
68 static void
69 linux_dmabuf_v1_buffer_destroy(struct ds_linux_dmabuf_v1_buffer *buffer)
70 {
71     int i;
72
73     for (i = 0; i < buffer->attributes.num_planes; i++) {
74         if (buffer->attributes.fd[i] == -1) continue;
75
76         close(buffer->attributes.fd[i]);
77     }
78
79     free(buffer);
80 }
81
82 static void
83 linux_dmabuf_v1_buffer_handle_destroy(struct wl_client *client, struct wl_resource *resource)
84 {
85     wl_resource_destroy(resource);
86 }
87
88 static const struct wl_buffer_interface linux_dmabuf_v1_buffer_impl = {
89     linux_dmabuf_v1_buffer_handle_destroy,
90 };
91
92 static void
93 linux_dmabuf_v1_buffer_iface_destroy(struct ds_buffer *ds_buffer)
94 {
95     struct ds_linux_dmabuf_v1_buffer *buffer;
96
97     buffer = wl_container_of(ds_buffer, buffer, base);
98     linux_dmabuf_v1_buffer_destroy(buffer);
99 }
100
101 static struct wl_resource *
102 linux_dmabuf_v1_buffer_iface_get_resource(struct ds_buffer *ds_buffer)
103 {
104     struct ds_linux_dmabuf_v1_buffer *buffer;
105
106     buffer = wl_container_of(ds_buffer, buffer, base);
107     return buffer->buffer_resource;
108 }
109
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,
113 };
114
115 static void linux_dmabuf_v1_handle_destroy(struct wl_client *client,
116         struct wl_resource *resource)
117 {
118     wl_resource_destroy(resource);
119 }
120
121 static void
122 linux_dmabuf_params_handle_resource_destroy(struct wl_resource *resource)
123 {
124     struct ds_linux_dmabuf_v1_buffer *buffer;
125
126     buffer = wl_resource_get_user_data(resource);
127     if (!buffer)
128         return;
129
130     linux_dmabuf_v1_buffer_destroy(buffer);
131 }
132
133 static void
134 linux_dmabuf_params_handle_destroy(struct wl_client *client,
135         struct wl_resource *params_resource)
136 {
137     wl_resource_destroy(params_resource);
138 }
139
140 static void
141 linux_dmabuf_params_handle_add(struct wl_client *client,
142         struct wl_resource *params_resource,
143         int32_t fd,
144         uint32_t plane_idx,
145         uint32_t offset,
146         uint32_t stride,
147         uint32_t modifier_hi,
148         uint32_t modifier_lo)
149 {
150     struct ds_linux_dmabuf_v1_buffer *buffer;
151
152     buffer = wl_resource_get_user_data(params_resource);
153     if (!buffer) {
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");
157         close(fd);
158         return;
159     }
160
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);
165         close(fd);
166         return;
167     }
168
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",
173                 plane_idx);
174         close(fd);
175         return;
176     }
177
178     buffer->attributes.fd[plane_idx] = fd;
179     buffer->attributes.offset[plane_idx] = offset;
180     buffer->attributes.stride[plane_idx] = stride;
181
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;
184     else
185         buffer->attributes.modifier[plane_idx] = ((uint64_t)modifier_hi << 32) | modifier_lo;
186
187     buffer->attributes.num_planes++;
188 }
189
190 static void
191 linux_dmabuf_v1_buffer_handle_resource_destroy(struct wl_resource *resource)
192 {
193     struct ds_linux_dmabuf_v1_buffer *buffer;
194
195     buffer = wl_resource_get_user_data(resource);
196     assert(buffer->buffer_resource == resource);
197     assert(!buffer->params_resource);
198
199     buffer->buffer_resource = NULL;
200     ds_buffer_drop(&buffer->base);
201 }
202
203 static void
204 linux_dmabuf_v1_buffer_handle_release(struct wl_listener *listener, void *data)
205 {
206     struct ds_linux_dmabuf_v1_buffer *buffer;
207
208     buffer = wl_container_of(listener, buffer, listener.buffer_release);
209     if (buffer->buffer_resource)
210         wl_buffer_send_release(buffer->buffer_resource);
211 }
212
213 static void
214 params_create_common(struct wl_client *client,
215         struct wl_resource *params_resource,
216         uint32_t buffer_id,
217         int32_t width,
218         int32_t height,
219         uint32_t format,
220         uint32_t flags)
221 {
222     struct ds_linux_dmabuf_v1_buffer *buffer;
223     int i;
224
225     buffer = wl_resource_get_user_data(params_resource);
226     if (!buffer) {
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");
230         return;
231     }
232
233     assert(buffer->params_resource == params_resource);
234     assert(!buffer->buffer_resource);
235
236     /* Switch the linux_dmabuf_v1_buffer object from params resource to
237      * eventually wl_buffer resource.
238      */
239     wl_resource_set_user_data(buffer->params_resource, NULL);
240     buffer->params_resource = NULL;
241
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");
246         goto err_out;
247     }
248
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);
255             goto err_out;
256         }
257     }
258
259     buffer->attributes.width = width;
260     buffer->attributes.height = height;
261     buffer->attributes.format = format;
262     buffer->attributes.flags = flags;
263
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);
268         goto err_out;
269     }
270
271     for (i = 0; i < buffer->attributes.num_planes; i++) {
272         off_t size;
273
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);
278             goto err_out;
279         }
280
281         if (i == 0 &&
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);
287             goto err_out;
288         }
289
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);
293         if (size == -1)
294             continue;
295
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);
301             goto err_out;
302         }
303
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);
309             goto err_out;
310         }
311
312         /* Only valid for first plane as other planes might be
313          * sub-sampled according to fourcc format */
314         if (i == 0 &&
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);
320             goto err_out;
321         }
322     }
323
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);
327         goto err_failed;
328     }
329
330     wl_resource_set_implementation(buffer->buffer_resource,
331             &linux_dmabuf_v1_buffer_impl,
332             buffer, linux_dmabuf_v1_buffer_handle_resource_destroy);
333
334     /* send 'created' event when the request is not for an immediate
335      * import, ie buffer_id is zero */
336     if (buffer_id == 0)
337         zwp_linux_buffer_params_v1_send_created(params_resource,
338                 buffer->buffer_resource);
339
340     ds_buffer_init(&buffer->base, &linux_dmabuf_v1_buffer_iface, width, height);
341
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);
346
347     return;
348
349 err_failed:
350     if (buffer_id == 0)
351         zwp_linux_buffer_params_v1_send_failed(params_resource);
352     else
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
357          * to be used.
358          */
359         wl_resource_post_error(params_resource,
360                 ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER,
361                 "importing the supplied dmabufs failed");
362
363 err_out:
364     linux_dmabuf_v1_buffer_destroy(buffer);
365 }
366
367
368 static void
369 linux_dmabuf_params_handle_create(struct wl_client *client,
370         struct wl_resource *params_resource,
371         int32_t width,
372         int32_t height,
373         uint32_t format,
374         uint32_t flags)
375 {
376     params_create_common(client, params_resource, 0, width, height, format,
377             flags);
378 }
379
380 static void
381 linux_dmabuf_params_handle_create_immed(struct wl_client *client,
382         struct wl_resource *params_resource,
383         uint32_t buffer_id,
384         int32_t width,
385         int32_t height,
386         uint32_t format,
387         uint32_t flags)
388 {
389     params_create_common(client, params_resource, buffer_id, width, height,
390             format, flags);
391 }
392
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,
398 };
399
400 static void
401 linux_dmabuf_v1_handle_create_params(struct wl_client *client,
402         struct wl_resource *linux_dmabuf_resource,
403         uint32_t id)
404 {
405     struct ds_linux_dmabuf_v1_buffer *buffer;
406     uint32_t version;
407     int i;
408
409     version = wl_resource_get_version(linux_dmabuf_resource);
410
411     buffer = calloc(1, sizeof *buffer);
412     if (!buffer) {
413         wl_client_post_no_memory(client);
414         return;
415     }
416
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);
421         free(buffer);
422         return;
423     }
424
425     for (i = 0; i < LINUX_DMABUF_MAX_PLANES; i++ )
426         buffer->attributes.fd[i] = -1;
427
428     wl_resource_set_implementation(buffer->params_resource,
429             &linux_dmabuf_params_impl, buffer,
430             linux_dmabuf_params_handle_resource_destroy);
431 }
432
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,
436 };
437
438 static void
439 linux_dmabuf_v1_send_formats(struct ds_linux_dmabuf_v1 *linux_dmabuf,
440         struct wl_resource *resource)
441 {
442     int i, j;
443
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);
450             } else {
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);
454             }
455         }
456     }
457 }
458
459 static void linux_dmabuf_v1_bind(struct wl_client *client, void *data,
460         uint32_t version, uint32_t id)
461 {
462     struct wl_resource *resource;
463         struct ds_linux_dmabuf_v1 *linux_dmabuf = data;
464
465     resource = wl_resource_create(client,
466             &zwp_linux_dmabuf_v1_interface, version, id);
467     if (!resource) {
468         wl_client_post_no_memory(client);
469         return;
470     }
471
472     wl_resource_set_implementation(resource, &linux_dmabuf_v1_impl, NULL, NULL);
473
474     linux_dmabuf_v1_send_formats(linux_dmabuf, resource);
475 }
476
477 static void
478 linux_dmabuf_v1_handle_display_destroy(struct wl_listener *listener, void *data)
479 {
480     struct ds_linux_dmabuf_v1 *linux_dmabuf;
481     int i;
482
483     linux_dmabuf = wl_container_of(listener, linux_dmabuf, display_destroy);
484     wl_global_destroy(linux_dmabuf->global);
485
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);
489     }
490
491     free(linux_dmabuf->supported_formats);
492     free(linux_dmabuf);
493 }
494
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)
498 {
499     struct ds_linux_dmabuf_v1 *linux_dmabuf;
500     int i, j;
501
502     DS_RETURN_VAL_IF_FAIL(display, NULL);
503
504     linux_dmabuf = calloc(1, sizeof *linux_dmabuf);
505     if (!linux_dmabuf)
506         return NULL;
507
508     linux_dmabuf->supported_formats = calloc(num_formats, sizeof *supported_formats);
509     if (!linux_dmabuf->supported_formats) {
510         goto failed;
511     }
512
513     linux_dmabuf->num_formats = num_formats;
514
515     for (i = 0; i < num_formats; i++) {
516         linux_dmabuf->supported_formats[i].format = supported_formats[i].format;
517
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)
521             goto failed;
522
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];
525
526         linux_dmabuf->supported_formats[i].num_modifiers = supported_formats[i].num_modifiers;
527     }
528
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)
533         goto failed;
534
535     linux_dmabuf->display_destroy.notify = linux_dmabuf_v1_handle_display_destroy;
536     wl_display_add_destroy_listener(display, &linux_dmabuf->display_destroy);
537
538     ds_buffer_register_resource_interface(&linux_dmabuf_resource_iface);
539
540     return linux_dmabuf;
541
542 failed:
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);
546     }
547
548     free(linux_dmabuf->supported_formats);
549     free(linux_dmabuf);
550
551     return NULL;
552 }
553
554 WL_EXPORT struct ds_linux_dmabuf_v1_buffer *
555 ds_linux_dmabuf_v1_buffer_from_buffer(struct ds_buffer *ds_buffer)
556 {
557     struct ds_linux_dmabuf_v1_buffer *buffer;
558
559     DS_RETURN_VAL_IF_FAIL(ds_buffer, NULL);
560
561     if (ds_buffer->iface != &linux_dmabuf_v1_buffer_iface)
562         return NULL;
563
564     return wl_container_of(ds_buffer, buffer, base);
565 }
566
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)
569 {
570     DS_RETURN_VAL_IF_FAIL(dmabuf_buffer, NULL);
571
572     return &dmabuf_buffer->attributes;
573 }