bo: use magic number for checking valid of bo
[platform/core/uifw/libtbm.git] / src / tbm_bo.c
1 /**************************************************************************
2
3 libtbm
4
5 Copyright 2012 Samsung Electronics co., Ltd. All Rights Reserved.
6
7 Contact: SooChan Lim <sc1.lim@samsung.com>, Sangjin Lee <lsj119@samsung.com>
8 Boram Park <boram1288.park@samsung.com>, Changyeon Lee <cyeon.lee@samsung.com>
9
10 Permission is hereby granted, free of charge, to any person obtaining a
11 copy of this software and associated documentation files (the
12 "Software"), to deal in the Software without restriction, including
13 without limitation the rights to use, copy, modify, merge, publish,
14 distribute, sub license, and/or sell copies of the Software, and to
15 permit persons to whom the Software is furnished to do so, subject to
16 the following conditions:
17
18 The above copyright notice and this permission notice (including the
19 next paragraph) shall be included in all copies or substantial portions
20 of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
25 IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
26 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
27 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
28 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
30 **************************************************************************/
31
32 #include "config.h"
33 #include "tbm_bufmgr.h"
34 #include "tbm_bufmgr_int.h"
35 #include "list.h"
36
37 #define TBM_BO_MAGIC 0xBF011234
38
39 /* check condition */
40 #define TBM_BO_RETURN_IF_FAIL(cond) {\
41         if (!(cond)) {\
42                 TBM_ERR("'%s' failed.\n", #cond);\
43                 _tbm_set_last_result(TBM_ERROR_INVALID_PARAMETER);\
44                 _tbm_bufmgr_mutex_unlock();\
45                 return;\
46         } \
47 }
48
49 #define TBM_BO_RETURN_VAL_IF_FAIL(cond, val) {\
50         if (!(cond)) {\
51                 TBM_ERR("'%s' failed.\n", #cond);\
52                 _tbm_set_last_result(TBM_ERROR_INVALID_PARAMETER);\
53                 _tbm_bufmgr_mutex_unlock();\
54                 return val;\
55         } \
56 }
57
58 static char *
59 _tbm_flag_to_str(int f)
60 {
61         static char str[255];
62
63         if (f == TBM_BO_DEFAULT)
64                  snprintf(str, 255, "DEFAULT");
65         else {
66                 int c = 0;
67
68                 if (f & TBM_BO_SCANOUT)
69                         c += snprintf(&str[c], 255-c, "SCANOUT");
70
71                 if (f & TBM_BO_NONCACHABLE) {
72                         if (c >= 0 && c < 255)
73                                 c += snprintf(&str[c], 255-c, ", ");
74
75                         if (c >= 0 && c < 255)
76                                 c += snprintf(&str[c], 255-c, "NONCACHABLE,");
77                 }
78
79                 if (f & TBM_BO_WC) {
80                         if (c >= 0 && c < 255)
81                                 c += snprintf(&str[c], 255-c, ", ");
82
83                         if (c >= 0 && c < 255)
84                                 c += snprintf(&str[c], 255-c, "WC");
85                 }
86         }
87
88         return str;
89 }
90
91 static void
92 _tbm_util_check_bo_cnt(tbm_bufmgr bufmgr)
93 {
94         static int last_chk_bo_cnt = 0;
95
96         if ((bufmgr->bo_cnt >= 500) && ((bufmgr->bo_cnt % 20) == 0) &&
97                 (bufmgr->bo_cnt > last_chk_bo_cnt)) {
98                 TBM_DBG("============TBM BO CNT DEBUG: bo_cnt=%d\n", bufmgr->bo_cnt);
99                 tbm_bufmgr_debug_show(bufmgr);
100                 last_chk_bo_cnt = bufmgr->bo_cnt;
101         }
102 }
103 /* LCOV_EXCL_STOP */
104
105 tbm_user_data *
106 user_data_lookup(struct list_head *user_data_list, unsigned long key)
107 {
108         tbm_user_data *old_data = NULL;
109
110         if (LIST_IS_EMPTY(user_data_list))
111                 return NULL;
112
113         LIST_FOR_EACH_ENTRY(old_data, user_data_list, item_link) {
114                 if (old_data->key == key)
115                         return old_data;
116         }
117
118         return NULL;
119 }
120
121 tbm_user_data *
122 user_data_create(unsigned long key, tbm_data_free data_free_func)
123 {
124         tbm_user_data *user_data;
125
126         user_data = calloc(1, sizeof(tbm_user_data));
127         if (!user_data) {
128                 /* LCOV_EXCL_START */
129                 TBM_ERR("fail to allocate an user_date\n");
130                 _tbm_set_last_result(TBM_ERROR_OUT_OF_MEMORY);
131                 return NULL;
132                 /* LCOV_EXCL_STOP */
133         }
134
135         user_data->key = key;
136         user_data->free_func = data_free_func;
137
138         return user_data;
139 }
140
141 void
142 user_data_delete(tbm_user_data *user_data)
143 {
144         if (user_data->data && user_data->free_func)
145                 user_data->free_func(user_data->data);
146
147         LIST_DEL(&user_data->item_link);
148
149         free(user_data);
150 }
151
152 static int
153 _bo_lock(tbm_bo bo, int device, int opt)
154 {
155         int ret = 1;
156         tbm_error_e error;
157
158         if (bo->bufmgr->backend_module_data) {
159                 if (bo->bufmgr->bo_func->bo_lock) {
160                         error = bo->bufmgr->bo_func->bo_lock(bo->bo_data, device, opt);
161                         if (error != TBM_ERROR_NONE) {
162                                 TBM_WRN("fail to lock");
163                                 _tbm_set_last_result(error);
164                                 ret = 0;
165                         }
166                 }
167         } else {
168                 if (bo->bufmgr->backend->bo_lock) {
169                         ret = bo->bufmgr->backend->bo_lock(bo, device, opt);
170                         if (!ret)
171                                 _tbm_set_last_result(TBM_ERROR_INVALID_OPERATION);
172                 }
173         }
174
175         return ret;
176 }
177
178 static void
179 _bo_unlock(tbm_bo bo)
180 {
181         tbm_error_e error;
182
183         if (bo->bufmgr->backend_module_data) {
184                 if (bo->bufmgr->bo_func->bo_unlock) {
185                         error = bo->bufmgr->bo_func->bo_unlock(bo->bo_data);
186                         if (error != TBM_ERROR_NONE) {
187                                 TBM_WRN("fail to unlock");
188                                 _tbm_set_last_result(error);
189                         }
190                 }
191         } else {
192                 if (bo->bufmgr->backend->bo_unlock)
193                         bo->bufmgr->backend->bo_unlock(bo);
194         }
195 }
196
197 static int
198 _tbm_bo_lock(tbm_bo bo, int device, int opt)
199 {
200         int old, ret;
201
202         if (!bo)
203                 return 0;
204
205         /* do not try to lock the bo */
206         if (bo->bufmgr->bo_lock_type == TBM_BUFMGR_BO_LOCK_TYPE_NEVER)
207                 return 1;
208
209         if (bo->lock_cnt < 0) {
210                 TBM_ERR("error bo:%p LOCK_CNT=%d\n",
211                         bo, bo->lock_cnt);
212                 return 0;
213         }
214
215         old = bo->lock_cnt;
216
217         switch (bo->bufmgr->bo_lock_type) {
218         case TBM_BUFMGR_BO_LOCK_TYPE_ONCE:
219                 if (bo->lock_cnt == 0) {
220                         _tbm_bufmgr_mutex_unlock();
221                         ret = _bo_lock(bo, device, opt);
222                         _tbm_bufmgr_mutex_lock();
223                         if (ret)
224                                 bo->lock_cnt++;
225                 } else
226                         ret = 1;
227                 break;
228         case TBM_BUFMGR_BO_LOCK_TYPE_ALWAYS:
229                 _tbm_bufmgr_mutex_unlock();
230                 ret = _bo_lock(bo, device, opt);
231                 _tbm_bufmgr_mutex_lock();
232                 if (ret)
233                         bo->lock_cnt++;
234                 break;
235         default:
236                 TBM_ERR("error bo:%p bo_lock_type[%d] is wrong.\n",
237                                 bo, bo->bufmgr->bo_lock_type);
238                 ret = 0;
239                 break;
240         }
241
242         TBM_DBG(">> LOCK bo:%p(%d->%d)\n", bo, old, bo->lock_cnt);
243
244         return ret;
245 }
246
247 static void
248 _tbm_bo_unlock(tbm_bo bo)
249 {
250         int old;
251
252         /* do not try to unlock the bo */
253         if (bo->bufmgr->bo_lock_type == TBM_BUFMGR_BO_LOCK_TYPE_NEVER)
254                 return;
255
256         old = bo->lock_cnt;
257
258         switch (bo->bufmgr->bo_lock_type) {
259         case TBM_BUFMGR_BO_LOCK_TYPE_ONCE:
260                 if (bo->lock_cnt > 0) {
261                         bo->lock_cnt--;
262                         if (bo->lock_cnt == 0)
263                                 _bo_unlock(bo);
264                 }
265                 break;
266         case TBM_BUFMGR_BO_LOCK_TYPE_ALWAYS:
267                 if (bo->lock_cnt > 0) {
268                         bo->lock_cnt--;
269                         _bo_unlock(bo);
270                 }
271                 break;
272         default:
273                 TBM_ERR("error bo:%p bo_lock_type[%d] is wrong.\n",
274                                 bo, bo->bufmgr->bo_lock_type);
275                 break;
276         }
277
278         if (bo->lock_cnt < 0)
279                 bo->lock_cnt = 0;
280
281         TBM_DBG(">> UNLOCK bo:%p(%d->%d)\n", bo, old, bo->lock_cnt);
282 }
283
284 static int
285 _tbm_bo_magic_check(tbm_bo bo)
286 {
287         if (bo->magic != TBM_BO_MAGIC)
288                 return 0;
289
290         return 1;
291 }
292
293 static int
294 _tbm_bo_is_valid(tbm_bo bo)
295 {
296         if (!bo) {
297                 TBM_ERR("error: bo is NULL.\n");
298                 return 0;
299         }
300
301         if (!_tbm_bo_magic_check(bo)) {
302                 TBM_ERR("error: No valid bo(%p).\n", bo);
303                 return 0;
304         }
305
306         return 1;
307 }
308
309 tbm_bo
310 tbm_bo_alloc(tbm_bufmgr bufmgr, int size, int flags)
311 {
312         tbm_bo bo;
313         void *bo_priv;
314         tbm_backend_bo_data *bo_data;
315         tbm_error_e error;
316
317         _tbm_bufmgr_mutex_lock();
318         _tbm_set_last_result(TBM_ERROR_NONE);
319
320         TBM_BO_RETURN_VAL_IF_FAIL(TBM_BUFMGR_IS_VALID(bufmgr), NULL);
321         TBM_BO_RETURN_VAL_IF_FAIL(size > 0, NULL);
322
323         bo = calloc(1, sizeof(struct _tbm_bo));
324         if (!bo) {
325                 /* LCOV_EXCL_START */
326                 TBM_ERR("error: fail to create of tbm_bo size(%d) flag(%s)\n",
327                                 size, _tbm_flag_to_str(flags));
328                 _tbm_set_last_result(TBM_ERROR_OUT_OF_MEMORY);
329                 _tbm_bufmgr_mutex_unlock();
330                 return NULL;
331                 /* LCOV_EXCL_STOP */
332         }
333
334         _tbm_util_check_bo_cnt(bufmgr);
335
336         bo->bufmgr = bufmgr;
337
338         if (bufmgr->backend_module_data) {
339                 bo_data = bufmgr->bufmgr_func->bufmgr_alloc_bo(bufmgr->bufmgr_data, (unsigned int)size, flags, &error);
340                 if (!bo_data) {
341                         /* LCOV_EXCL_START */
342                         TBM_ERR("error: fail to create of tbm_bo size(%d) flag(%s)\n",
343                                         size, _tbm_flag_to_str(flags));
344                         _tbm_set_last_result(error);
345                         free(bo);
346                         _tbm_bufmgr_mutex_unlock();
347                         return NULL;
348                         /* LCOV_EXCL_STOP */
349                 }
350                 bo->bo_data = bo_data;
351         } else {
352                 bo_priv = bo->bufmgr->backend->bo_alloc(bo, size, flags);
353                 if (!bo_priv) {
354                         /* LCOV_EXCL_START */
355                         TBM_ERR("error: fail to create of tbm_bo size(%d) flag(%s)\n",
356                                         size, _tbm_flag_to_str(flags));
357                         _tbm_set_last_result(TBM_ERROR_INVALID_OPERATION);
358                         free(bo);
359                         _tbm_bufmgr_mutex_unlock();
360                         return NULL;
361                         /* LCOV_EXCL_STOP */
362                 }
363                 bo->priv = bo_priv;
364         }
365
366         bo->bufmgr->bo_cnt++;
367         bo->magic = TBM_BO_MAGIC;
368         bo->ref_cnt = 1;
369         bo->flags = flags;
370
371         TBM_TRACE_BO("bo(%p) size(%d) refcnt(%d), flag(%s)\n", bo, size, bo->ref_cnt,
372                         _tbm_flag_to_str(bo->flags));
373
374         LIST_INITHEAD(&bo->user_data_list);
375
376         LIST_ADD(&bo->item_link, &bo->bufmgr->bo_list);
377
378         _tbm_bufmgr_mutex_unlock();
379
380         return bo;
381 }
382
383 tbm_bo
384 tbm_bo_ref(tbm_bo bo)
385 {
386         _tbm_bufmgr_mutex_lock();
387         _tbm_set_last_result(TBM_ERROR_NONE);
388
389         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), NULL);
390
391         bo->ref_cnt++;
392
393         TBM_TRACE_BO("bo(%p) ref_cnt(%d)\n", bo, bo->ref_cnt);
394
395         _tbm_bufmgr_mutex_unlock();
396
397         return bo;
398 }
399
400 void
401 tbm_bo_unref(tbm_bo bo)
402 {
403         _tbm_bufmgr_mutex_lock();
404         _tbm_set_last_result(TBM_ERROR_NONE);
405
406         TBM_BO_RETURN_IF_FAIL(_tbm_bo_is_valid(bo));
407
408         TBM_TRACE_BO("bo(%p) ref_cnt(%d)\n", bo, bo->ref_cnt - 1);
409
410         if (bo->ref_cnt <= 0) {
411                 _tbm_bufmgr_mutex_unlock();
412                 return;
413         }
414
415         bo->ref_cnt--;
416         if (bo->ref_cnt == 0)
417                 _tbm_bo_free(bo);
418
419         _tbm_bufmgr_mutex_unlock();
420 }
421
422 tbm_bo_handle
423 tbm_bo_map(tbm_bo bo, int device, int opt)
424 {
425         tbm_bo_handle bo_handle;
426         tbm_error_e error;
427
428         _tbm_bufmgr_mutex_lock();
429         _tbm_set_last_result(TBM_ERROR_NONE);
430
431         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), (tbm_bo_handle) NULL);
432
433         if (!_tbm_bo_lock(bo, device, opt)) {
434                 TBM_ERR("error: fail to lock bo:%p)\n", bo);
435                 _tbm_bufmgr_mutex_unlock();
436                 return (tbm_bo_handle) NULL;
437         }
438
439         if (bo->bufmgr->backend_module_data) {
440                 bo_handle = bo->bufmgr->bo_func->bo_map(bo->bo_data, device, opt, &error);
441                 if (bo_handle.ptr == NULL) {
442                         /* LCOV_EXCL_START */
443                         _tbm_set_last_result(error);
444                         TBM_ERR("error: fail to map bo:%p error:%d\n", bo, error);
445                         _tbm_bo_unlock(bo);
446                         _tbm_bufmgr_mutex_unlock();
447                         return (tbm_bo_handle) NULL;
448                         /* LCOV_EXCL_STOP */
449                 }
450         } else {
451                 bo_handle = bo->bufmgr->backend->bo_map(bo, device, opt);
452                 if (bo_handle.ptr == NULL) {
453                         /* LCOV_EXCL_START */
454                         _tbm_set_last_result(TBM_ERROR_INVALID_OPERATION);
455                         TBM_ERR("error: fail to map bo:%p\n", bo);
456                         _tbm_bo_unlock(bo);
457                         _tbm_bufmgr_mutex_unlock();
458                         return (tbm_bo_handle) NULL;
459                         /* LCOV_EXCL_STOP */
460                 }
461         }
462
463         /* increase the map_count */
464         bo->map_cnt++;
465
466         TBM_TRACE_BO("bo(%p) map_cnt(%d)\n", bo, bo->map_cnt);
467
468         _tbm_bufmgr_mutex_unlock();
469
470         return bo_handle;
471 }
472
473 int
474 tbm_bo_unmap(tbm_bo bo)
475 {
476         int ret = 1;
477         tbm_error_e error;
478
479         _tbm_bufmgr_mutex_lock();
480         _tbm_set_last_result(TBM_ERROR_NONE);
481
482         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), 0);
483         TBM_BO_RETURN_VAL_IF_FAIL(bo->map_cnt > 0, 0);
484
485         if (bo->bufmgr->backend_module_data) {
486                 error = bo->bufmgr->bo_func->bo_unmap(bo->bo_data);
487                 if (error != TBM_ERROR_NONE) {
488                         /* LCOV_EXCL_START */
489                         TBM_ERR("error: bo(%p) map_cnt(%d) error(%d)\n", bo, bo->map_cnt, error);
490                         _tbm_set_last_result(error);
491                         ret = 0;
492                         _tbm_bufmgr_mutex_unlock();
493                         return ret;
494                         /* LCOV_EXCL_STOP */
495                 }
496         } else {
497                 ret = bo->bufmgr->backend->bo_unmap(bo);
498                 if (!ret) {
499                         /* LCOV_EXCL_START */
500                         TBM_ERR("error: bo(%p) map_cnt(%d)\n", bo, bo->map_cnt);
501                         _tbm_set_last_result(TBM_ERROR_INVALID_OPERATION);
502                         _tbm_bufmgr_mutex_unlock();
503                         return ret;
504                         /* LCOV_EXCL_STOP */
505                 }
506         }
507
508         /* decrease the map_count */
509         bo->map_cnt--;
510
511         TBM_TRACE_BO("bo(%p) map_cnt(%d)\n", bo, bo->map_cnt);
512
513         _tbm_bo_unlock(bo);
514
515         _tbm_bufmgr_mutex_unlock();
516
517         return ret;
518 }
519
520 tbm_bo_handle
521 tbm_bo_get_handle(tbm_bo bo, int device)
522 {
523         tbm_bo_handle bo_handle;
524         tbm_error_e error;
525
526         _tbm_bufmgr_mutex_lock();
527         _tbm_set_last_result(TBM_ERROR_NONE);
528
529         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), (tbm_bo_handle) NULL);
530
531         if (bo->bufmgr->backend_module_data) {
532                 bo_handle = bo->bufmgr->bo_func->bo_get_handle(bo->bo_data, device, &error);
533                 if (bo_handle.ptr == NULL) {
534                         /* LCOV_EXCL_START */
535                         TBM_ERR("error: bo(%p) bo_handle(%p) error(%d)\n", bo, bo_handle.ptr, error);
536                         _tbm_set_last_result(error);
537                         _tbm_bufmgr_mutex_unlock();
538                         return (tbm_bo_handle) NULL;
539                         /* LCOV_EXCL_STOP */
540                 }
541         } else {
542                 bo_handle = bo->bufmgr->backend->bo_get_handle(bo, device);
543                 if (bo_handle.ptr == NULL) {
544                         /* LCOV_EXCL_START */
545                         TBM_ERR("error: bo(%p) bo_handle(%p)\n", bo, bo_handle.ptr);
546                         _tbm_set_last_result(TBM_ERROR_INVALID_OPERATION);
547                         _tbm_bufmgr_mutex_unlock();
548                         return (tbm_bo_handle) NULL;
549                         /* LCOV_EXCL_STOP */
550                 }
551         }
552
553         TBM_TRACE_BO("bo(%p) bo_handle(%p)\n", bo, bo_handle.ptr);
554
555         _tbm_bufmgr_mutex_unlock();
556
557         return bo_handle;
558 }
559
560 tbm_key
561 tbm_bo_export(tbm_bo bo)
562 {
563         tbm_key ret;
564         tbm_error_e error;
565
566         _tbm_bufmgr_mutex_lock();
567         _tbm_set_last_result(TBM_ERROR_NONE);
568
569         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), 0);
570
571         if (bo->bufmgr->backend_module_data) {
572                 if (!bo->bufmgr->bo_func->bo_export_key) {
573                         /* LCOV_EXCL_START */
574                         _tbm_bufmgr_mutex_unlock();
575                         _tbm_set_last_result(TBM_ERROR_NOT_SUPPORTED);
576                         return 0;
577                         /* LCOV_EXCL_STOP */
578                 }
579
580                 ret = bo->bufmgr->bo_func->bo_export_key(bo->bo_data, &error);
581                 if (!ret) {
582                         /* LCOV_EXCL_START */
583                         TBM_ERR("error: bo(%p) tbm_key(%d) error(%d)\n", bo, ret, error);
584                         _tbm_set_last_result(error);
585                         _tbm_bufmgr_mutex_unlock();
586                         return ret;
587                         /* LCOV_EXCL_STOP */
588                 }
589         } else {
590                 if (!bo->bufmgr->backend->bo_export) {
591                         /* LCOV_EXCL_START */
592                         _tbm_bufmgr_mutex_unlock();
593                         _tbm_set_last_result(TBM_ERROR_NOT_SUPPORTED);
594                         return 0;
595                         /* LCOV_EXCL_STOP */
596                 }
597
598                 ret = bo->bufmgr->backend->bo_export(bo);
599                 if (!ret) {
600                         /* LCOV_EXCL_START */
601                         TBM_ERR("error: bo(%p) tbm_key(%d)\n", bo, ret);
602                         _tbm_set_last_result(TBM_ERROR_INVALID_OPERATION);
603                         _tbm_bufmgr_mutex_unlock();
604                         return ret;
605                         /* LCOV_EXCL_STOP */
606                 }
607         }
608
609         TBM_TRACE_BO("bo(%p) tbm_key(%u)\n", bo, ret);
610
611         _tbm_bufmgr_mutex_unlock();
612
613         return ret;
614 }
615
616 tbm_fd
617 tbm_bo_export_fd(tbm_bo bo)
618 {
619         tbm_fd ret;
620         tbm_error_e error;
621
622         _tbm_bufmgr_mutex_lock();
623         _tbm_set_last_result(TBM_ERROR_NONE);
624
625         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), -1);
626
627         if (bo->bufmgr->backend_module_data) {
628                 if (!bo->bufmgr->bo_func->bo_export_fd) {
629                         /* LCOV_EXCL_START */
630                         _tbm_bufmgr_mutex_unlock();
631                         _tbm_set_last_result(TBM_ERROR_NOT_SUPPORTED);
632                         return -1;
633                         /* LCOV_EXCL_STOP */
634                 }
635
636                 ret = bo->bufmgr->bo_func->bo_export_fd(bo->bo_data, &error);
637                 if (ret < 0) {
638                         /* LCOV_EXCL_START */
639                         TBM_ERR("error: bo(%p) tbm_fd(%d) error(%d)\n", bo, ret, error);
640                         _tbm_set_last_result(error);
641                         _tbm_bufmgr_mutex_unlock();
642                         return ret;
643                         /* LCOV_EXCL_STOP */
644                 }
645         } else {
646                 if (!bo->bufmgr->backend->bo_export_fd) {
647                         /* LCOV_EXCL_START */
648                         _tbm_bufmgr_mutex_unlock();
649                         _tbm_set_last_result(TBM_ERROR_NOT_SUPPORTED);
650                         return -1;
651                         /* LCOV_EXCL_STOP */
652                 }
653
654                 ret = bo->bufmgr->backend->bo_export_fd(bo);
655                 if (ret < 0) {
656                         /* LCOV_EXCL_START */
657                         TBM_ERR("error: bo(%p) tbm_fd(%d)\n", bo, ret);
658                         _tbm_set_last_result(TBM_ERROR_INVALID_OPERATION);
659                         _tbm_bufmgr_mutex_unlock();
660                         return ret;
661                         /* LCOV_EXCL_STOP */
662                 }
663         }
664
665         TBM_TRACE_BO("bo(%p) tbm_fd(%d)\n", bo, ret);
666
667         _tbm_bufmgr_mutex_unlock();
668
669         return ret;
670 }
671
672 tbm_bo
673 tbm_bo_import(tbm_bufmgr bufmgr, unsigned int key)
674 {
675         tbm_bo bo;
676         tbm_bo bo2 = NULL;
677         void *bo_priv;
678         tbm_error_e error;
679         tbm_backend_bo_data *bo_data;
680
681         _tbm_bufmgr_mutex_lock();
682         _tbm_set_last_result(TBM_ERROR_NONE);
683
684         TBM_BO_RETURN_VAL_IF_FAIL(TBM_BUFMGR_IS_VALID(bufmgr), NULL);
685
686         if (bufmgr->backend_module_data) {
687                 if (!bufmgr->bufmgr_func->bufmgr_import_key) {
688                         /* LCOV_EXCL_START */
689                         _tbm_bufmgr_mutex_unlock();
690                         _tbm_set_last_result(TBM_ERROR_NOT_SUPPORTED);
691                         return NULL;
692                         /* LCOV_EXCL_STOP */
693                 }
694         } else {
695                 if (!bufmgr->backend->bo_import) {
696                         /* LCOV_EXCL_START */
697                         _tbm_bufmgr_mutex_unlock();
698                         _tbm_set_last_result(TBM_ERROR_NOT_SUPPORTED);
699                         return NULL;
700                         /* LCOV_EXCL_STOP */
701                 }
702         }
703
704         _tbm_util_check_bo_cnt(bufmgr);
705
706         bo = calloc(1, sizeof(struct _tbm_bo));
707         if (!bo) {
708                 /* LCOV_EXCL_START */
709                 TBM_ERR("error: fail to import of tbm_bo by key(%d)\n", key);
710                 _tbm_set_last_result(TBM_ERROR_OUT_OF_MEMORY);
711                 _tbm_bufmgr_mutex_unlock();
712                 return NULL;
713                 /* LCOV_EXCL_STOP */
714         }
715
716         bo->bufmgr = bufmgr;
717
718         if (bo->bufmgr->backend_module_data) {
719                 bo_data = bo->bufmgr->bufmgr_func->bufmgr_import_key(bufmgr->bufmgr_data, key, &error);
720                 if (!bo_data) {
721                         /* LCOV_EXCL_START */
722                         TBM_ERR("error: fail to import of tbm_bo by key(%d). error(%d)\n", key, error);
723                         _tbm_set_last_result(error);
724                         free(bo);
725                         _tbm_bufmgr_mutex_unlock();
726                         return NULL;
727                         /* LCOV_EXCL_STOP */
728                 }
729
730                 if (!LIST_IS_EMPTY(&bo->bufmgr->bo_list)) {
731                         LIST_FOR_EACH_ENTRY(bo2, &bo->bufmgr->bo_list, item_link) {
732                                 if (bo2->bo_data == bo_data) {
733                                         TBM_TRACE_BO("find bo(%p) ref(%d) key(%d) flag(%s) in list\n",
734                                                         bo2, bo2->ref_cnt, key,
735                                                         _tbm_flag_to_str(bo2->flags));
736                                         bo2->ref_cnt++;
737                                         free(bo);
738                                         _tbm_bufmgr_mutex_unlock();
739                                         return bo2;
740                                 }
741                         }
742                 }
743                 bo->bo_data = bo_data;
744         } else {
745                 bo_priv = bo->bufmgr->backend->bo_import(bo, key);
746                 if (!bo_priv) {
747                         /* LCOV_EXCL_START */
748                         TBM_ERR("error: fail to import of tbm_bo by key(%d)\n", key);
749                         _tbm_set_last_result(TBM_ERROR_INVALID_OPERATION);
750                         free(bo);
751                         _tbm_bufmgr_mutex_unlock();
752                         return NULL;
753                         /* LCOV_EXCL_STOP */
754                 }
755
756                 if (!LIST_IS_EMPTY(&bo->bufmgr->bo_list)) {
757                         LIST_FOR_EACH_ENTRY(bo2, &bo->bufmgr->bo_list, item_link) {
758                                 if (bo2->priv == bo_priv) {
759                                         TBM_TRACE_BO("find bo(%p) ref(%d) key(%d) flag(%s) in list\n",
760                                                         bo2, bo2->ref_cnt, key,
761                                                         _tbm_flag_to_str(bo2->flags));
762                                         bo2->ref_cnt++;
763                                         free(bo);
764                                         _tbm_bufmgr_mutex_unlock();
765                                         return bo2;
766                                 }
767                         }
768                 }
769                 bo->priv = bo_priv;
770         }
771
772         bo->bufmgr->bo_cnt++;
773         bo->magic = TBM_BO_MAGIC;
774         bo->ref_cnt = 1;
775
776         if (bo->bufmgr->backend_module_data) {
777                 bo->flags = bo->bufmgr->bo_func->bo_get_memory_types(bo->bo_data, &error);
778                 if (error != TBM_ERROR_NONE) {
779                         TBM_ERR("fail to get the bo flags(memory_types)");
780                         _tbm_set_last_result(error);
781                         bo->flags = TBM_BO_DEFAULT;
782                 }
783         } else {
784                 if (bo->bufmgr->backend->bo_get_flags)
785                         bo->flags = bo->bufmgr->backend->bo_get_flags(bo);
786                 else
787                         bo->flags = TBM_BO_DEFAULT;
788         }
789
790         TBM_TRACE_BO("import new bo(%p) ref(%d) key(%d) flag(%s) in list\n",
791                           bo, bo->ref_cnt, key, _tbm_flag_to_str(bo->flags));
792
793         LIST_INITHEAD(&bo->user_data_list);
794
795         LIST_ADD(&bo->item_link, &bo->bufmgr->bo_list);
796
797         _tbm_bufmgr_mutex_unlock();
798
799         return bo;
800 }
801
802 tbm_bo
803 tbm_bo_import_fd(tbm_bufmgr bufmgr, tbm_fd fd)
804 {
805         tbm_bo bo;
806         tbm_bo bo2 = NULL;
807         void *bo_priv;
808         tbm_backend_bo_data *bo_data;
809         tbm_error_e error;
810
811         _tbm_bufmgr_mutex_lock();
812         _tbm_set_last_result(TBM_ERROR_NONE);
813
814         TBM_BO_RETURN_VAL_IF_FAIL(TBM_BUFMGR_IS_VALID(bufmgr), NULL);
815
816         if (bufmgr->backend_module_data) {
817                 if (!bufmgr->bufmgr_func->bufmgr_import_fd) {
818                         /* LCOV_EXCL_START */
819                         _tbm_bufmgr_mutex_unlock();
820                         _tbm_set_last_result(TBM_ERROR_NOT_SUPPORTED);
821                         return NULL;
822                         /* LCOV_EXCL_STOP */
823                 }
824         } else {
825                 if (!bufmgr->backend->bo_import_fd) {
826                         /* LCOV_EXCL_START */
827                         _tbm_bufmgr_mutex_unlock();
828                         _tbm_set_last_result(TBM_ERROR_NOT_SUPPORTED);
829                         return NULL;
830                         /* LCOV_EXCL_STOP */
831                 }
832         }
833
834         _tbm_util_check_bo_cnt(bufmgr);
835
836         bo = calloc(1, sizeof(struct _tbm_bo));
837         if (!bo) {
838                 /* LCOV_EXCL_START */
839                 TBM_ERR("error: fail to import tbm_bo by tbm_fd(%d)\n", fd);
840                 _tbm_set_last_result(TBM_ERROR_OUT_OF_MEMORY);
841                 _tbm_bufmgr_mutex_unlock();
842                 return NULL;
843                 /* LCOV_EXCL_STOP */
844         }
845
846         bo->bufmgr = bufmgr;
847
848         if (bo->bufmgr->backend_module_data) {
849                 bo_data = bo->bufmgr->bufmgr_func->bufmgr_import_fd(bufmgr->bufmgr_data, fd, &error);
850                 if (!bo_data) {
851                         /* LCOV_EXCL_START */
852                         TBM_ERR("error: fail to import tbm_bo by tbm_fd(%d). error(%d)\n", fd, error);
853                         _tbm_set_last_result(error);
854                         free(bo);
855                         _tbm_bufmgr_mutex_unlock();
856                         return NULL;
857                         /* LCOV_EXCL_STOP */
858                 }
859
860                 if (!LIST_IS_EMPTY(&bo->bufmgr->bo_list)) {
861                         LIST_FOR_EACH_ENTRY(bo2, &bo->bufmgr->bo_list, item_link) {
862                                 if (bo2->bo_data == bo_data) {
863                                         TBM_TRACE_BO("find bo(%p) ref(%d) fd(%d) flag(%s) in list\n",
864                                                         bo2, bo2->ref_cnt, fd,
865                                                         _tbm_flag_to_str(bo2->flags));
866                                         bo2->ref_cnt++;
867                                         free(bo);
868                                         _tbm_bufmgr_mutex_unlock();
869                                         return bo2;
870                                 }
871                         }
872                 }
873                 bo->bo_data = bo_data;
874         } else {
875                 bo_priv = bo->bufmgr->backend->bo_import_fd(bo, fd);
876                 if (!bo_priv) {
877                         /* LCOV_EXCL_START */
878                         TBM_ERR("error: fail to import tbm_bo by tbm_fd(%d)\n", fd);
879                         _tbm_set_last_result(TBM_ERROR_INVALID_OPERATION);
880                         free(bo);
881                         _tbm_bufmgr_mutex_unlock();
882                         return NULL;
883                         /* LCOV_EXCL_STOP */
884                 }
885
886                 if (!LIST_IS_EMPTY(&bo->bufmgr->bo_list)) {
887                         LIST_FOR_EACH_ENTRY(bo2, &bo->bufmgr->bo_list, item_link) {
888                                 if (bo2->priv == bo_priv) {
889                                         TBM_TRACE_BO("find bo(%p) ref(%d) fd(%d) flag(%s) in list\n",
890                                                         bo2, bo2->ref_cnt, fd,
891                                                         _tbm_flag_to_str(bo2->flags));
892                                         bo2->ref_cnt++;
893                                         free(bo);
894                                         _tbm_bufmgr_mutex_unlock();
895                                         return bo2;
896                                 }
897                         }
898                 }
899                 bo->priv = bo_priv;
900         }
901
902         bo->bufmgr->bo_cnt++;
903         bo->magic = TBM_BO_MAGIC;
904         bo->ref_cnt = 1;
905
906         if (bo->bufmgr->backend_module_data) {
907                 bo->flags = bo->bufmgr->bo_func->bo_get_memory_types(bo->bo_data, &error);
908                 if (error != TBM_ERROR_NONE) {
909                         TBM_ERR("fail to get the bo flags(memory_types)");
910                         _tbm_set_last_result(error);
911                         bo->flags = TBM_BO_DEFAULT;
912                 }
913         } else {
914                 if (bo->bufmgr->backend->bo_get_flags)
915                         bo->flags = bo->bufmgr->backend->bo_get_flags(bo);
916                 else
917                         bo->flags = TBM_BO_DEFAULT;
918         }
919
920         TBM_TRACE_BO("import bo(%p) ref(%d) fd(%d) flag(%s)\n",
921                         bo, bo->ref_cnt, fd, _tbm_flag_to_str(bo->flags));
922
923         LIST_INITHEAD(&bo->user_data_list);
924
925         LIST_ADD(&bo->item_link, &bo->bufmgr->bo_list);
926
927         _tbm_bufmgr_mutex_unlock();
928
929         return bo;
930 }
931
932 int
933 tbm_bo_size(tbm_bo bo)
934 {
935         int size;
936         tbm_error_e error;
937
938         _tbm_bufmgr_mutex_lock();
939         _tbm_set_last_result(TBM_ERROR_NONE);
940
941         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), 0);
942
943         if (bo->bufmgr->backend_module_data) {
944                 size = bo->bufmgr->bo_func->bo_get_size(bo->bo_data, &error);
945                 if (error != TBM_ERROR_NONE) {
946                         TBM_ERR("fail to get the size of the bo_data(%d).", error);
947                         _tbm_set_last_result(TBM_ERROR_NONE);
948                 }
949         } else
950                 size = bo->bufmgr->backend->bo_size(bo);
951
952         TBM_TRACE_BO("bo(%p) size(%d)\n", bo, size);
953
954         _tbm_bufmgr_mutex_unlock();
955
956         return size;
957 }
958
959 int
960 tbm_bo_locked(tbm_bo bo)
961 {
962         _tbm_bufmgr_mutex_lock();
963         _tbm_set_last_result(TBM_ERROR_NONE);
964
965         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), 0);
966
967         if (bo->bufmgr->bo_lock_type == TBM_BUFMGR_BO_LOCK_TYPE_NEVER) {
968                 TBM_ERR("bo(%p) lock_cnt(%d)\n", bo, bo->lock_cnt);
969                 _tbm_set_last_result(TBM_ERROR_INVALID_OPERATION);
970                 _tbm_bufmgr_mutex_unlock();
971                 return 0;
972         }
973
974         if (bo->lock_cnt > 0) {
975                 TBM_TRACE_BO("error: bo(%p) lock_cnt(%d)\n", bo, bo->lock_cnt);
976                 _tbm_bufmgr_mutex_unlock();
977                 return 1;
978         }
979
980         TBM_TRACE_BO("bo(%p) lock_cnt(%d)\n", bo, bo->lock_cnt);
981         _tbm_bufmgr_mutex_unlock();
982
983         return 0;
984 }
985
986 int
987 tbm_bo_swap(tbm_bo bo1, tbm_bo bo2)
988 {
989         tbm_error_e error1, error2;
990         int size1 = -1, size2 = -2;
991         void *temp;
992
993         _tbm_bufmgr_mutex_lock();
994         _tbm_set_last_result(TBM_ERROR_NONE);
995
996         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo1), 0);
997         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo2), 0);
998
999         TBM_TRACE_BO("before: bo1(%p) bo2(%p)\n", bo1, bo2);
1000
1001         if (bo1->bufmgr->backend_module_data) {
1002                 size1 = bo1->bufmgr->bo_func->bo_get_size(bo1->bo_data, &error1);
1003                 if (error1 != TBM_ERROR_NONE) {
1004                         TBM_ERR("fail to get the size of bo1.(%d)", error1);
1005                         _tbm_set_last_result(error1);
1006                         goto fail;
1007                 }
1008                 size2 = bo2->bufmgr->bo_func->bo_get_size(bo2->bo_data, &error2);
1009                 if (error2 != TBM_ERROR_NONE) {
1010                         TBM_ERR("fail to get the size of bo2.(%d)", error2);
1011                         _tbm_set_last_result(error2);
1012                         goto fail;
1013                 }
1014         } else {
1015                 size1 = bo1->bufmgr->backend->bo_size(bo1);
1016                 size2 = bo2->bufmgr->backend->bo_size(bo2);
1017         }
1018
1019         if (size1 != size2) {
1020                 TBM_ERR("error: bo1 size(%d) and bo2 size(%d) is different.", size1, size2);
1021                 _tbm_set_last_result(TBM_ERROR_INVALID_OPERATION);
1022                 goto fail;
1023         }
1024
1025         TBM_TRACE_BO("after: bo1(%p) bo2(%p)\n", bo1, bo2);
1026
1027         temp = bo1->priv;
1028         bo1->priv = bo2->priv;
1029         bo2->priv = temp;
1030
1031         _tbm_bufmgr_mutex_unlock();
1032
1033         return 1;
1034
1035 fail:
1036         TBM_ERR("error: bo1(%p) bo2(%p)\n", bo1, bo2);
1037         _tbm_bufmgr_mutex_unlock();
1038
1039         return 0;
1040 }
1041
1042 int
1043 tbm_bo_add_user_data(tbm_bo bo, unsigned long key,
1044                      tbm_data_free data_free_func)
1045 {
1046         tbm_user_data *data;
1047
1048         _tbm_bufmgr_mutex_lock();
1049         _tbm_set_last_result(TBM_ERROR_NONE);
1050
1051         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), 0);
1052
1053         /* check if the data according to the key exist if so, return false. */
1054         data = user_data_lookup(&bo->user_data_list, key);
1055         if (data) {
1056                 TBM_TRACE_BO("warning: user data already exist key(%ld)\n", key);
1057                 _tbm_set_last_result(TBM_ERROR_INVALID_PARAMETER);
1058                 _tbm_bufmgr_mutex_unlock();
1059                 return 0;
1060         }
1061
1062         data = user_data_create(key, data_free_func);
1063         if (!data) {
1064                 TBM_ERR("error: bo(%p) key(%lu)\n", bo, key);
1065                 _tbm_set_last_result(TBM_ERROR_INVALID_PARAMETER);
1066                 _tbm_bufmgr_mutex_unlock();
1067                 return 0;
1068         }
1069
1070         TBM_TRACE_BO("bo(%p) key(%lu) data(%p)\n", bo, key, data->data);
1071
1072         LIST_ADD(&data->item_link, &bo->user_data_list);
1073
1074         _tbm_bufmgr_mutex_unlock();
1075
1076         return 1;
1077 }
1078
1079 int
1080 tbm_bo_delete_user_data(tbm_bo bo, unsigned long key)
1081 {
1082         tbm_user_data *old_data;
1083
1084         _tbm_bufmgr_mutex_lock();
1085         _tbm_set_last_result(TBM_ERROR_NONE);
1086
1087         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), 0);
1088
1089         if (LIST_IS_EMPTY(&bo->user_data_list)) {
1090                 TBM_TRACE_BO("bo(%p) key(%lu)\n", bo, key);
1091                 _tbm_set_last_result(TBM_ERROR_INVALID_PARAMETER);
1092                 _tbm_bufmgr_mutex_unlock();
1093                 return 0;
1094         }
1095
1096         old_data = user_data_lookup(&bo->user_data_list, key);
1097         if (!old_data) {
1098                 TBM_TRACE_BO("bo(%p) key(%lu)\n", bo, key);
1099                 _tbm_set_last_result(TBM_ERROR_INVALID_PARAMETER);
1100                 _tbm_bufmgr_mutex_unlock();
1101                 return 0;
1102         }
1103
1104         TBM_TRACE_BO("bo(%p) key(%lu) data(%p)\n", bo, key, old_data->data);
1105
1106         user_data_delete(old_data);
1107
1108         _tbm_bufmgr_mutex_unlock();
1109
1110         return 1;
1111 }
1112
1113 int
1114 tbm_bo_set_user_data(tbm_bo bo, unsigned long key, void *data)
1115 {
1116         tbm_user_data *old_data;
1117
1118         _tbm_bufmgr_mutex_lock();
1119         _tbm_set_last_result(TBM_ERROR_NONE);
1120
1121         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), 0);
1122
1123         if (LIST_IS_EMPTY(&bo->user_data_list)) {
1124                 TBM_TRACE_BO("error: bo(%p) key(%lu)\n", bo, key);
1125                 _tbm_set_last_result(TBM_ERROR_INVALID_PARAMETER);
1126                 _tbm_bufmgr_mutex_unlock();
1127                 return 0;
1128         }
1129
1130         old_data = user_data_lookup(&bo->user_data_list, key);
1131         if (!old_data) {
1132                 TBM_TRACE_BO("error: bo(%p) key(%lu)\n", bo, key);
1133                 _tbm_set_last_result(TBM_ERROR_INVALID_PARAMETER);
1134                 _tbm_bufmgr_mutex_unlock();
1135                 return 0;
1136         }
1137
1138         if (old_data->data && old_data->free_func)
1139                 old_data->free_func(old_data->data);
1140         old_data->data = data;
1141
1142         TBM_TRACE_BO("bo(%p) key(%lu) data(%p)\n", bo, key, old_data->data);
1143
1144         _tbm_bufmgr_mutex_unlock();
1145
1146         return 1;
1147 }
1148
1149 int
1150 tbm_bo_get_user_data(tbm_bo bo, unsigned long key, void **data)
1151 {
1152         tbm_user_data *old_data;
1153
1154         _tbm_bufmgr_mutex_lock();
1155         _tbm_set_last_result(TBM_ERROR_NONE);
1156
1157         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), 0);
1158
1159         if (!data || LIST_IS_EMPTY(&bo->user_data_list)) {
1160                 TBM_TRACE_BO("error: bo(%p) key(%lu)\n", bo, key);
1161                 _tbm_set_last_result(TBM_ERROR_INVALID_PARAMETER);
1162                 _tbm_bufmgr_mutex_unlock();
1163                 return 0;
1164         }
1165
1166         old_data = user_data_lookup(&bo->user_data_list, key);
1167         if (!old_data) {
1168                 *data = NULL;
1169                 TBM_TRACE_BO("error: bo(%p) key(%lu)\n", bo, key);
1170                 _tbm_set_last_result(TBM_ERROR_INVALID_PARAMETER);
1171                 _tbm_bufmgr_mutex_unlock();
1172                 return 0;
1173         }
1174
1175         *data = old_data->data;
1176
1177         TBM_TRACE_BO("bo(%p) key(%lu) data(%p)\n", bo, key, old_data->data);
1178
1179         _tbm_bufmgr_mutex_unlock();
1180
1181         return 1;
1182 }
1183
1184 int
1185 tbm_bo_get_flags(tbm_bo bo)
1186 {
1187         int flags;
1188
1189         _tbm_bufmgr_mutex_lock();
1190
1191         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), 0);
1192
1193         flags = bo->flags;
1194
1195         TBM_TRACE_BO("bo(%p)\n", bo);
1196
1197         _tbm_bufmgr_mutex_unlock();
1198
1199         return flags;
1200 }
1201
1202 /* LCOV_EXCL_START */
1203 /* internal function */
1204 int
1205 _tbm_bo_set_surface(tbm_bo bo, tbm_surface_h surface)
1206 {
1207         _tbm_bufmgr_mutex_lock();
1208
1209         TBM_BO_RETURN_VAL_IF_FAIL(_tbm_bo_is_valid(bo), 0);
1210
1211         bo->surface = surface;
1212
1213         _tbm_bufmgr_mutex_unlock();
1214
1215         return 1;
1216 }
1217
1218 void
1219 _tbm_bo_free(tbm_bo bo)
1220 {
1221         /* destory the user_data_list */
1222         if (!LIST_IS_EMPTY(&bo->user_data_list)) {
1223                 tbm_user_data *old_data = NULL, *tmp;
1224
1225                 LIST_FOR_EACH_ENTRY_SAFE(old_data, tmp,
1226                                 &bo->user_data_list, item_link) {
1227                         TBM_DBG("free user_data\n");
1228                         user_data_delete(old_data);
1229                 }
1230         }
1231
1232         while (bo->lock_cnt > 0) {
1233                 TBM_ERR("error lock_cnt:%d\n", bo->lock_cnt);
1234                 _bo_unlock(bo);
1235                 bo->lock_cnt--;
1236         }
1237
1238         /* call the bo_free */
1239         if (bo->bufmgr->backend_module_data) {
1240                 bo->bufmgr->bo_func->bo_free(bo->bo_data);
1241                 bo->bo_data = NULL;
1242         } else {
1243                 bo->bufmgr->backend->bo_free(bo);
1244                 bo->priv = NULL;
1245         }
1246
1247         bo->magic = 0;
1248         bo->bufmgr->bo_cnt--;
1249
1250         LIST_DEL(&bo->item_link);
1251         free(bo);
1252 }
1253 /* LCOV_EXCL_STOP */