source: fix memory leak & null pointer dereference
[platform/adaptation/emulator/gst-plugins-emulator.git] / src / gstmaruinterface3.c
1 /*
2  * GStreamer codec plugin for Tizen Emulator.
3  *
4  * Copyright (C) 2013 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact:
7  * KiTae Kim <kt920.kim@samsung.com>
8  * SeokYeon Hwang <syeon.hwang@samsung.com>
9  * YeongKyoon Lee <yeongkyoon.lee@samsung.com>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public
22  * License along with this library; if not, write to the
23  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24  * Boston, MA 02111-1307, USA.
25  *
26  * Contributors:
27  * - S-Core Co., Ltd
28  *
29  */
30
31 #include <inttypes.h>
32 #include "gstmaru.h"
33 #include "gstmaruinterface.h"
34 #include "gstmaruutils.h"
35 #include "gstmarumem.h"
36 #include "gstmarudevice.h"
37
38 Interface *interface = NULL;
39
40 enum IOCTL_CMD {
41   IOCTL_CMD_GET_VERSION,
42   IOCTL_CMD_GET_ELEMENTS_SIZE,
43   IOCTL_CMD_GET_ELEMENTS,
44   IOCTL_CMD_GET_CONTEXT_INDEX,
45   IOCTL_CMD_SECURE_BUFFER,
46   IOCTL_CMD_TRY_SECURE_BUFFER,
47   IOCTL_CMD_RELEASE_BUFFER,
48   IOCTL_CMD_INVOKE_API_AND_GET_DATA,
49   IOCTL_CMD_GET_PROFILE_STATUS,
50 };
51
52 typedef struct {
53   uint32_t  api_index;
54   uint32_t  ctx_index;
55   uintptr_t  mem_offset;
56   int32_t  buffer_size;
57 } __attribute__((packed)) IOCTL_Data;
58
59 #define BRILLCODEC_KEY         'B'
60 #define IOCTL_RW(CMD)           (_IOWR(BRILLCODEC_KEY, CMD, IOCTL_Data))
61
62 #define CODEC_META_DATA_SIZE    256
63 #define GET_OFFSET(buffer)      ((uintptr_t)buffer - (uintptr_t)device_mem)
64 #define SMALLDATA               0
65
66 #define OFFSET_PICTURE_BUFFER   0x100
67
68 static inline bool can_use_new_decode_api(void) {
69     if (CHECK_VERSION(3)) {
70         return true;
71     }
72     return false;
73 }
74
75 static int
76 invoke_device_api(int fd, int32_t ctx_index, int32_t api_index,
77                           uintptr_t *mem_offset, int32_t buffer_size)
78 {
79   GST_DEBUG (" >> Enter");
80   IOCTL_Data ioctl_data = { 0, };
81   int ret = -1;
82
83   ioctl_data.api_index = api_index;
84   ioctl_data.ctx_index = ctx_index;
85   if (mem_offset) {
86     ioctl_data.mem_offset = *mem_offset;
87   }
88   ioctl_data.buffer_size = buffer_size;
89
90   ret = ioctl(fd, IOCTL_RW(IOCTL_CMD_INVOKE_API_AND_GET_DATA), &ioctl_data);
91
92   if (mem_offset) {
93     *mem_offset = ioctl_data.mem_offset;
94   }
95
96   GST_DEBUG (" >> Leave");
97   return ret;
98 }
99
100 static int
101 secure_device_mem (int fd, guint ctx_id, guint buf_size, gpointer* buffer)
102 {
103   GST_DEBUG (" >> Enter");
104   int ret = 0;
105   IOCTL_Data data;
106
107   data.ctx_index = ctx_id;
108   data.buffer_size = buf_size;
109
110   ret = ioctl (fd, IOCTL_RW(IOCTL_CMD_SECURE_BUFFER), &data);
111
112   *buffer = (gpointer)(device_mem + data.mem_offset);
113   GST_DEBUG ("device_mem %p, offset_size 0x%"PRIXPTR, device_mem, data.mem_offset);
114
115   GST_DEBUG (" >> Leave");
116   return ret;
117 }
118
119 static void
120 release_device_mem (int fd, gpointer start)
121 {
122   GST_DEBUG (" >> Enter");
123   int ret;
124   uint32_t offset = start - device_mem;
125
126   GST_DEBUG ("release device_mem start: %p, offset: 0x%x", start, offset);
127   ret = ioctl (fd, IOCTL_RW(IOCTL_CMD_RELEASE_BUFFER), &offset);
128   if (ret < 0) {
129     GST_ERROR ("failed to release buffer\n");
130   }
131   GST_DEBUG (" >> Leave");
132 }
133
134 static int
135 get_context_index (int fd)
136 {
137   int ctx_index;
138
139   if (ioctl (fd, IOCTL_RW(IOCTL_CMD_GET_CONTEXT_INDEX), &ctx_index) < 0) {
140     GST_ERROR ("failed to get a context index, %d", fd);
141     return -1;
142   }
143
144   return ctx_index;
145 }
146 // TODO: check this code is needed
147 #if 0
148 static void
149 buffer_free (gpointer start)
150 {
151   release_device_mem (device_fd, start);
152 }
153
154 static void
155 buffer_free2 (gpointer start)
156 {
157   release_device_mem (device_fd, start - OFFSET_PICTURE_BUFFER);
158 }
159 #endif
160 static inline void fill_size_header(void *buffer, size_t size)
161 {
162   *((uint32_t *)buffer) = (uint32_t)size;
163 }
164
165 //
166 // Interface
167 // INIT / DEINIT
168 //
169
170 static int
171 init (CodecContext *ctx, CodecElement *codec, CodecDevice *dev)
172 {
173   int opened = 0;
174   gpointer buffer = NULL;
175   int ret;
176   uintptr_t mem_offset;
177
178   GST_DEBUG (" >> Enter");
179   if ((ctx->index = get_context_index(dev->fd)) <= 0) {
180     GST_ERROR ("failed to get a context index");
181     return -1;
182   }
183   GST_DEBUG ("get context index: %d, %d", ctx->index, dev->fd);
184
185   /* buffer size is 0. It means that this function is required to
186    * use small size.
187   */
188   if (secure_device_mem(dev->fd, ctx->index, 0, &buffer) < 0) {
189     GST_ERROR ("failed to get a memory block");
190     return -1;
191   }
192
193   codec_init_data_to (ctx, codec, buffer);
194
195   mem_offset = GET_OFFSET(buffer);
196   ret = invoke_device_api (dev->fd, ctx->index, CODEC_INIT, &mem_offset, SMALLDATA);
197
198   if (ret < 0) {
199     GST_ERROR ("invoke_device_api failed");
200     return -1;
201   }
202
203   opened =
204     codec_init_data_from (ctx, codec->media_type, device_mem + mem_offset);
205
206   if (opened < 0) {
207     GST_ERROR ("failed to open Context for %s", codec->name);
208   } else {
209     ctx->codec = codec;
210   }
211
212   release_device_mem(dev->fd, device_mem + mem_offset);
213
214   GST_DEBUG (" >> Leave");
215   return opened;
216 }
217
218 static void
219 deinit (CodecContext *ctx, CodecDevice *dev)
220 {
221   GST_INFO ("close context %d", ctx->index);
222   invoke_device_api (dev->fd, ctx->index, CODEC_DEINIT, NULL, -1);
223 }
224
225 //
226 // Interface
227 // VIDEO DECODE / ENCODE
228 //
229
230 struct video_decode_input {
231     int32_t inbuf_size;
232     int32_t idx;
233     int64_t in_offset;
234     uint8_t inbuf;          // for pointing inbuf address
235 } __attribute__((packed));
236
237 struct video_decode_output {
238     int32_t len;
239     int32_t got_picture;
240     uint8_t data;           // for pointing data address
241 } __attribute__((packed));
242
243 static int
244 decode_video (GstMaruVidDec *marudec, uint8_t *inbuf, int inbuf_size,
245                     gint idx, gint64 in_offset, GstBuffer **out_buf, int *have_data)
246 {
247   GST_DEBUG (" >> Enter");
248   CodecContext *ctx = marudec->context;
249   CodecDevice *dev = marudec->dev;
250   int len = 0, ret = 0;
251   gpointer buffer = NULL;
252   uintptr_t mem_offset;
253   size_t size = sizeof(inbuf_size) + sizeof(idx) + sizeof(in_offset) + inbuf_size;
254
255   ret = secure_device_mem(dev->fd, ctx->index, size, &buffer);
256   if (ret < 0) {
257     GST_ERROR ("failed to get available memory to write inbuf");
258     return -1;
259   }
260
261   fill_size_header(buffer, size);
262   struct video_decode_input *decode_input = buffer + sizeof(int32_t);
263   decode_input->inbuf_size = inbuf_size;
264   decode_input->idx = idx;
265   decode_input->in_offset = in_offset;
266   memcpy(&decode_input->inbuf, inbuf, inbuf_size);
267
268   mem_offset = GET_OFFSET(buffer);
269
270   marudec->is_using_new_decode_api = (can_use_new_decode_api() && (ctx->video.pix_fmt != -1));
271   if (marudec->is_using_new_decode_api) {
272     int picture_size = gst_maru_avpicture_size (ctx->video.pix_fmt,
273         ctx->video.width, ctx->video.height);
274     if (picture_size < 0) {
275       // can not enter here...
276       GST_ERROR ("Can not enter here. Check about it !!!");
277       picture_size = SMALLDATA;
278     }
279     ret = invoke_device_api(dev->fd, ctx->index, CODEC_DECODE_VIDEO_AND_PICTURE_COPY, &mem_offset, picture_size);
280   } else {
281     // in case of this, a decoded frame is not given from codec device.
282     ret = invoke_device_api(dev->fd, ctx->index, CODEC_DECODE_VIDEO, &mem_offset, SMALLDATA);
283   }
284
285   if (ret < 0) {
286     GST_ERROR ("invoke API failed");
287     return -1;
288   }
289
290   struct video_decode_output *decode_output = device_mem + mem_offset;
291   len = decode_output->len;
292   *have_data = decode_output->got_picture;
293   memcpy(&ctx->video, &decode_output->data, sizeof(VideoData));
294
295   GST_DEBUG_OBJECT (marudec, "after decode: len %d, have_data %d",
296         len, *have_data);
297
298   if (len >= 0 && *have_data > 0 && marudec->is_using_new_decode_api) {
299     marudec->is_last_buffer = ret;
300     marudec->mem_offset = mem_offset;
301   } else {
302     release_device_mem(dev->fd, device_mem + mem_offset);
303   }
304
305   GST_DEBUG (" >> Leave");
306   return len;
307 }
308
309 GstFlowReturn
310 alloc_and_copy (GstMaruVidDec *marudec, guint64 offset, guint size,
311                   GstCaps *caps, GstBuffer **buf)
312 {
313   GST_DEBUG (" >> enter");
314   bool is_last_buffer = 0;
315   uintptr_t mem_offset;
316   CodecContext *ctx;
317   CodecDevice *dev;
318   GstMapInfo mapinfo;
319
320   ctx = marudec->context;
321   dev = marudec->dev;
322
323   if (marudec->is_using_new_decode_api) {
324     is_last_buffer = marudec->is_last_buffer;
325     mem_offset = marudec->mem_offset;
326   } else {
327     ctx = marudec->context;
328
329     mem_offset = 0;
330
331     int ret = invoke_device_api(dev->fd, ctx->index, CODEC_PICTURE_COPY, &mem_offset, size);
332     if (ret < 0) {
333       GST_DEBUG ("failed to get available buffer");
334       return GST_FLOW_ERROR;
335     }
336     is_last_buffer = ret;
337   }
338
339   gpointer *buffer = NULL;
340   is_last_buffer = 1;
341   if (is_last_buffer) {
342     // FIXME: we must aligned buffer offset.
343     //buffer = g_malloc (size);
344     gst_buffer_map (*buf, &mapinfo, GST_MAP_READWRITE);
345
346     if (marudec->is_using_new_decode_api) {
347       memcpy (mapinfo.data, device_mem + mem_offset + OFFSET_PICTURE_BUFFER, size);
348     } else {
349       memcpy (mapinfo.data, device_mem + mem_offset, size);
350     }
351     release_device_mem(dev->fd, device_mem + mem_offset);
352
353     GST_DEBUG ("secured last buffer!! Use heap buffer");
354   } else {
355     // address of "device_mem" and "opaque" is aleady aligned.
356     if (marudec->is_using_new_decode_api) {
357       buffer = (gpointer)(device_mem + mem_offset + OFFSET_PICTURE_BUFFER);
358       //GST_BUFFER_FREE_FUNC (*buf) = buffer_free2;
359     } else {
360       buffer = (gpointer)(device_mem + mem_offset);
361       //GST_BUFFER_FREE_FUNC (*buf) = buffer_free;
362     }
363
364
365     GST_DEBUG ("device memory start: 0x%p, offset 0x%"PRIXPTR, (void *) buffer, mem_offset);
366   }
367
368   gst_buffer_unmap (*buf, &mapinfo);
369
370   GST_DEBUG (" >> leave");
371   return GST_FLOW_OK;
372 }
373
374 static GstFlowReturn
375 buffer_alloc_and_copy (GstPad *pad, guint64 offset, guint size,
376                   GstCaps *caps, GstBuffer **buf)
377 {
378   GST_DEBUG (" >> enter");
379   bool is_last_buffer = 0;
380   uintptr_t mem_offset;
381   GstMaruDec *marudec;
382   CodecContext *ctx;
383   CodecDevice *dev;
384   GstMapInfo mapinfo;
385
386   marudec = (GstMaruDec *)gst_pad_get_element_private(pad);
387   ctx = marudec->context;
388   dev = marudec->dev;
389
390   if (marudec->is_using_new_decode_api) {
391     is_last_buffer = marudec->is_last_buffer;
392     mem_offset = marudec->mem_offset;
393   } else {
394     ctx = marudec->context;
395
396     mem_offset = 0;
397
398     GST_DEBUG ("buffer_and_copy. ctx_id: %d", ctx->index);
399
400     int ret = invoke_device_api(dev->fd, ctx->index, CODEC_PICTURE_COPY, &mem_offset, size);
401     if (ret < 0) {
402       GST_DEBUG ("failed to get available buffer");
403       return GST_FLOW_ERROR;
404     }
405     is_last_buffer = ret;
406   }
407
408   gpointer *buffer = NULL;
409   is_last_buffer = 1;
410   if (is_last_buffer) {
411     // FIXME: we must aligned buffer offset.
412     buffer = g_malloc (size);
413
414     if (marudec->is_using_new_decode_api) {
415       memcpy (buffer, device_mem + mem_offset + OFFSET_PICTURE_BUFFER, size);
416     } else {
417       memcpy (buffer, device_mem + mem_offset, size);
418     }
419     release_device_mem(dev->fd, device_mem + mem_offset);
420
421     GST_DEBUG ("secured last buffer!! Use heap buffer");
422   } else {
423     // address of "device_mem" and "opaque" is aleady aligned.
424     if (marudec->is_using_new_decode_api) {
425       buffer = (gpointer)(device_mem + mem_offset + OFFSET_PICTURE_BUFFER);
426       //GST_BUFFER_FREE_FUNC (*buf) = buffer_free2;
427     } else {
428       buffer = (gpointer)(device_mem + mem_offset);
429       //GST_BUFFER_FREE_FUNC (*buf) = buffer_free;
430     }
431
432
433     GST_DEBUG ("device memory start: 0x%p, offset 0x%"PRIXPTR, (void *) buffer, mem_offset);
434   }
435
436   *buf = gst_buffer_new ();
437   //*buf = gst_buffer_new_and_alloc (size);
438   gst_buffer_map (*buf, &mapinfo, GST_MAP_READWRITE);
439   mapinfo.data = (guint8 *)buffer;
440   mapinfo.size = size;
441   GST_BUFFER_OFFSET (*buf) = offset;
442   gst_buffer_unmap (*buf, &mapinfo);
443
444   GST_DEBUG (" >> leave");
445   return GST_FLOW_OK;
446 }
447
448 struct video_encode_input {
449     int32_t inbuf_size;
450     int64_t in_timestamp;
451     uint8_t inbuf;          // for pointing inbuf address
452 } __attribute__((packed));
453
454 struct video_encode_output {
455     int32_t len;
456     int32_t coded_frame;
457     int32_t key_frame;
458     uint8_t data;           // for pointing data address
459 } __attribute__((packed));
460
461 static int
462 encode_video (CodecContext *ctx, uint8_t *outbuf,
463                     int out_size, uint8_t *inbuf,
464                     int inbuf_size, int64_t in_timestamp,
465                     int *coded_frame, int *is_keyframe,
466                     CodecDevice *dev)
467 {
468   int len = 0, ret = 0;
469   gpointer buffer = NULL;
470   uintptr_t mem_offset;
471   size_t size = sizeof(inbuf_size) + sizeof(in_timestamp) + inbuf_size;
472
473   ret = secure_device_mem(dev->fd, ctx->index, size, &buffer);
474   if (ret < 0) {
475     GST_ERROR ("failed to small size of buffer");
476     return -1;
477   }
478
479   fill_size_header(buffer, size);
480   struct video_encode_input *encode_input = buffer + sizeof(int32_t);
481   encode_input->inbuf_size = inbuf_size;
482   encode_input->in_timestamp = in_timestamp;
483   memcpy(&encode_input->inbuf, inbuf, inbuf_size);
484   GST_DEBUG ("insize: %d, inpts: %lld", encode_input->inbuf_size,(long long) encode_input->in_timestamp);
485
486   mem_offset = GET_OFFSET(buffer);
487
488   ret = invoke_device_api(dev->fd, ctx->index, CODEC_ENCODE_VIDEO, &mem_offset, SMALLDATA);
489
490   if (ret < 0) {
491     GST_ERROR ("Invoke API failed");
492     return -1;
493   }
494
495   GST_DEBUG ("encode_video. mem_offset = 0x%"PRIXPTR, mem_offset);
496
497   struct video_encode_output *encode_output = device_mem + mem_offset;
498   len = encode_output->len;
499   *coded_frame = encode_output->coded_frame;
500   *is_keyframe = encode_output->key_frame;
501   memcpy(outbuf, &encode_output->data, len);
502
503   release_device_mem(dev->fd, device_mem + mem_offset);
504
505   return len;
506 }
507
508 //
509 // Interface
510 // AUDIO DECODE / ENCODE
511 //
512
513 struct audio_decode_input {
514     int32_t inbuf_size;
515     uint8_t inbuf;          // for pointing inbuf address
516 } __attribute__((packed));
517
518 struct audio_decode_output {
519     int32_t len;
520     int32_t got_frame;
521     uint8_t data;           // for pointing data address
522 } __attribute__((packed));
523
524 struct audio_encode_input {
525     int32_t inbuf_size;
526     uint8_t inbuf;          // for pointing inbuf address
527 } __attribute__((packed));
528
529 struct audio_encode_output {
530     int32_t len;
531     uint8_t data;           // for pointing data address
532 } __attribute__((packed));
533
534 static int
535 decode_audio (CodecContext *ctx, int16_t *samples,
536                     int *have_data, uint8_t *inbuf,
537                     int inbuf_size, CodecDevice *dev)
538 {
539   int len = 0, ret = 0;
540   gpointer buffer = NULL;
541   uintptr_t mem_offset;
542   size_t size = sizeof(inbuf_size) + inbuf_size;
543
544   ret = secure_device_mem(dev->fd, ctx->index, size, &buffer);
545   if (ret < 0) {
546     GST_ERROR ("failed to get available memory to write inbuf");
547     return -1;
548   }
549
550   GST_DEBUG ("decode_audio. in_buffer size %d", inbuf_size);
551
552   fill_size_header(buffer, size);
553   struct audio_decode_input *decode_input = buffer + sizeof(int32_t);
554   decode_input->inbuf_size = inbuf_size;
555   memcpy(&decode_input->inbuf, inbuf, inbuf_size);
556
557   mem_offset = GET_OFFSET(buffer);
558
559   ret = invoke_device_api(dev->fd, ctx->index, CODEC_DECODE_AUDIO, &mem_offset, SMALLDATA);
560
561   if (ret < 0) {
562     return -1;
563   }
564
565   GST_DEBUG ("decode_audio. ctx_id: %d, buffer = 0x%p",
566     ctx->index, device_mem + mem_offset);
567
568   struct audio_decode_output *decode_output = device_mem + mem_offset;
569   len = decode_output->len;
570   *have_data = decode_output->got_frame;
571   memcpy(&ctx->audio, &decode_output->data, sizeof(AudioData));
572
573   memcpy (samples, device_mem + mem_offset + OFFSET_PICTURE_BUFFER, len);
574
575   GST_DEBUG ("decode_audio. sample_fmt %d sample_rate %d, channels %d, ch_layout %" PRIu64 ", len %d",
576           ctx->audio.sample_fmt, ctx->audio.sample_rate, ctx->audio.channels,
577           ctx->audio.channel_layout, len);
578
579   release_device_mem(dev->fd, device_mem + mem_offset);
580
581   return len;
582 }
583
584 static int
585 encode_audio (CodecContext *ctx, uint8_t *outbuf,
586                     int max_size, uint8_t *inbuf,
587                     int inbuf_size, int64_t timestamp,
588                     CodecDevice *dev)
589 {
590   int len = 0, ret = 0;
591   gpointer buffer = NULL;
592   uintptr_t mem_offset;
593   size_t size = sizeof(inbuf_size) + inbuf_size;
594
595   ret = secure_device_mem(dev->fd, ctx->index, inbuf_size, &buffer);
596   if (ret < 0) {
597     GST_ERROR ("failed to get available memory to write inbuf");
598     return -1;
599   }
600
601   fill_size_header(buffer, size);
602   struct audio_encode_input *encode_input = buffer + sizeof(int32_t);
603   encode_input->inbuf_size = inbuf_size;
604   memcpy(&encode_input->inbuf, inbuf, inbuf_size);
605
606   mem_offset = GET_OFFSET(buffer);
607
608   ret = invoke_device_api(dev->fd, ctx->index, CODEC_ENCODE_AUDIO, &mem_offset, SMALLDATA);
609
610   if (ret < 0) {
611     return -1;
612   }
613
614   GST_DEBUG ("encode_audio. mem_offset = 0x%"PRIXPTR, mem_offset);
615
616   struct audio_encode_output *encode_output = device_mem + mem_offset;
617   len = encode_output->len;
618   if (len > 0) {
619     memcpy (outbuf, &encode_output->data, len);
620   }
621
622   GST_DEBUG ("encode_audio. len: %d", len);
623
624   release_device_mem(dev->fd, device_mem + mem_offset);
625
626   return len;
627 }
628
629 //
630 // Interface
631 // MISC
632 //
633
634 static void
635 flush_buffers (CodecContext *ctx, CodecDevice *dev)
636 {
637   GST_DEBUG ("flush buffers of context: %d", ctx->index);
638   invoke_device_api (dev->fd, ctx->index, CODEC_FLUSH_BUFFERS, NULL, -1);
639 }
640
641 static int
642 get_device_version (int fd)
643 {
644   uint32_t device_version;
645   int ret;
646
647   ret = ioctl (fd, IOCTL_RW(IOCTL_CMD_GET_VERSION), &device_version);
648   if (ret < 0) {
649     return ret;
650   }
651
652   return device_version;
653 }
654
655 static GList *
656 prepare_elements (int fd)
657 {
658   uint32_t size = 0;
659   int ret, elem_cnt, i;
660   GList *elements = NULL;
661   CodecElement *elem;
662
663   ret = ioctl (fd, IOCTL_RW(IOCTL_CMD_GET_ELEMENTS_SIZE), &size);
664   if (ret < 0) {
665     GST_ERROR ("get_elements_size failed");
666     return NULL;
667   }
668
669   elem = g_malloc(size);
670
671   ret = ioctl (fd, IOCTL_RW(IOCTL_CMD_GET_ELEMENTS), elem);
672   if (ret < 0) {
673     GST_ERROR ("get_elements failed");
674     g_free (elem);
675     return NULL;
676   }
677
678   elem_cnt = size / sizeof(CodecElement);
679   for (i = 0; i < elem_cnt; i++) {
680     elements = g_list_append (elements, &elem[i]);
681   }
682
683   g_free (elem);
684   return elements;
685 }
686
687 static int
688 get_profile_status (int fd)
689 {
690   uint8_t profile_status;
691   int ret;
692
693   ret = ioctl (fd, IOCTL_RW(IOCTL_CMD_GET_PROFILE_STATUS), &profile_status);
694   if (ret < 0) {
695     return ret;
696   }
697
698   return profile_status;
699 }
700
701 // Interfaces
702 Interface *interface_version_3 = &(Interface) {
703   .init = init,
704   .deinit = deinit,
705   .decode_video = decode_video,
706   .decode_audio = decode_audio,
707   .encode_video = encode_video,
708   .encode_audio = encode_audio,
709   .flush_buffers = flush_buffers,
710   .buffer_alloc_and_copy = buffer_alloc_and_copy,
711   .get_device_version = get_device_version,
712   .prepare_elements = prepare_elements,
713   .get_profile_status = get_profile_status,
714 };