1 /*
2  * Cogl
3  *
4  * A Low Level GPU Graphics and Utilities API
5  *
6  * Copyright (C) 2020 Endless, Inc.
7  *
8  * Permission is hereby granted, free of charge, to any person
9  * obtaining a copy of this software and associated documentation
10  * files (the "Software"), to deal in the Software without
11  * restriction, including without limitation the rights to use, copy,
12  * modify, merge, publish, distribute, sublicense, and/or sell copies
13  * of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be
17  * included in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26  * SOFTWARE.
27  *
28  * Authors:
29  *   Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
30  */
31 
32 #include "cogl-config.h"
33 
34 #include "cogl-dma-buf-handle.h"
35 #include "cogl-object.h"
36 
37 #include <errno.h>
38 #include <gio/gio.h>
39 #ifdef __linux__
40 #include <linux/dma-buf.h>
41 #include <sys/ioctl.h>
42 #endif
43 #include <sys/mman.h>
44 #include <unistd.h>
45 
46 struct _CoglDmaBufHandle
47 {
48   CoglFramebuffer *framebuffer;
49   int dmabuf_fd;
50   int width;
51   int height;
52   int stride;
53   int offset;
54   int bpp;
55   gpointer user_data;
56   GDestroyNotify destroy_func;
57 };
58 
59 CoglDmaBufHandle *
cogl_dma_buf_handle_new(CoglFramebuffer * framebuffer,int dmabuf_fd,int width,int height,int stride,int offset,int bpp,gpointer user_data,GDestroyNotify destroy_func)60 cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer,
61                          int              dmabuf_fd,
62                          int              width,
63                          int              height,
64                          int              stride,
65                          int              offset,
66                          int              bpp,
67                          gpointer         user_data,
68                          GDestroyNotify   destroy_func)
69 {
70   CoglDmaBufHandle *dmabuf_handle;
71 
72   g_assert (framebuffer);
73   g_assert (dmabuf_fd != -1);
74 
75   dmabuf_handle = g_new0 (CoglDmaBufHandle, 1);
76   dmabuf_handle->framebuffer = g_object_ref (framebuffer);
77   dmabuf_handle->dmabuf_fd = dmabuf_fd;
78   dmabuf_handle->user_data = user_data;
79   dmabuf_handle->destroy_func = destroy_func;
80 
81   dmabuf_handle->width = width;
82   dmabuf_handle->height = height;
83   dmabuf_handle->stride = stride;
84   dmabuf_handle->offset = offset;
85   dmabuf_handle->bpp = bpp;
86 
87   return dmabuf_handle;
88 }
89 
90 void
cogl_dma_buf_handle_free(CoglDmaBufHandle * dmabuf_handle)91 cogl_dma_buf_handle_free (CoglDmaBufHandle *dmabuf_handle)
92 {
93   g_return_if_fail (dmabuf_handle != NULL);
94 
95   g_clear_object (&dmabuf_handle->framebuffer);
96 
97   if (dmabuf_handle->destroy_func)
98     g_clear_pointer (&dmabuf_handle->user_data, dmabuf_handle->destroy_func);
99 
100   if (dmabuf_handle->dmabuf_fd != -1)
101     close (dmabuf_handle->dmabuf_fd);
102 
103   g_free (dmabuf_handle);
104 }
105 
106 #ifdef __linux__
107 static gboolean
sync_read(CoglDmaBufHandle * dmabuf_handle,uint64_t start_or_end,GError ** error)108 sync_read (CoglDmaBufHandle  *dmabuf_handle,
109            uint64_t           start_or_end,
110            GError           **error)
111 {
112   struct dma_buf_sync sync = { 0 };
113 
114   sync.flags = start_or_end | DMA_BUF_SYNC_READ;
115 
116   while (TRUE)
117     {
118       int ret;
119 
120       ret = ioctl (dmabuf_handle->dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync);
121       if (ret == -1 && errno == EINTR)
122         {
123           continue;
124         }
125       else if (ret == -1)
126         {
127           g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
128                        "ioctl: %s", g_strerror (errno));
129           return FALSE;
130         }
131       else
132         {
133           break;
134         }
135     }
136 
137   return TRUE;
138 }
139 
140 gboolean
cogl_dma_buf_handle_sync_read_start(CoglDmaBufHandle * dmabuf_handle,GError ** error)141 cogl_dma_buf_handle_sync_read_start (CoglDmaBufHandle  *dmabuf_handle,
142                                      GError           **error)
143 {
144   return sync_read (dmabuf_handle, DMA_BUF_SYNC_START, error);
145 }
146 
147 gboolean
cogl_dma_buf_handle_sync_read_end(CoglDmaBufHandle * dmabuf_handle,GError ** error)148 cogl_dma_buf_handle_sync_read_end (CoglDmaBufHandle  *dmabuf_handle,
149                                    GError           **error)
150 {
151   return sync_read (dmabuf_handle, DMA_BUF_SYNC_END, error);
152 }
153 #endif
154 
155 gpointer
cogl_dma_buf_handle_mmap(CoglDmaBufHandle * dmabuf_handle,GError ** error)156 cogl_dma_buf_handle_mmap (CoglDmaBufHandle  *dmabuf_handle,
157                           GError           **error)
158 {
159   size_t size;
160   gpointer data;
161 
162   size = dmabuf_handle->height * dmabuf_handle->stride;
163 
164   data = mmap (NULL, size, PROT_READ, MAP_PRIVATE,
165                dmabuf_handle->dmabuf_fd,
166                dmabuf_handle->offset);
167   if (data == MAP_FAILED)
168     {
169       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
170                    "mmap failed: %s", g_strerror (errno));
171       return NULL;
172     }
173 
174   return data;
175 }
176 
177 gboolean
cogl_dma_buf_handle_munmap(CoglDmaBufHandle * dmabuf_handle,gpointer data,GError ** error)178 cogl_dma_buf_handle_munmap (CoglDmaBufHandle  *dmabuf_handle,
179                             gpointer           data,
180                             GError           **error)
181 {
182   size_t size;
183 
184   size = dmabuf_handle->height * dmabuf_handle->stride;
185   if (munmap (data, size) != 0)
186     {
187       g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
188                    "munmap failed: %s", g_strerror (errno));
189       return FALSE;
190     }
191 
192   return TRUE;
193 }
194 
195 CoglFramebuffer *
cogl_dma_buf_handle_get_framebuffer(CoglDmaBufHandle * dmabuf_handle)196 cogl_dma_buf_handle_get_framebuffer (CoglDmaBufHandle *dmabuf_handle)
197 {
198   return dmabuf_handle->framebuffer;
199 }
200 
201 int
cogl_dma_buf_handle_get_fd(CoglDmaBufHandle * dmabuf_handle)202 cogl_dma_buf_handle_get_fd (CoglDmaBufHandle *dmabuf_handle)
203 {
204   return dmabuf_handle->dmabuf_fd;
205 }
206 
207 int
cogl_dma_buf_handle_get_width(CoglDmaBufHandle * dmabuf_handle)208 cogl_dma_buf_handle_get_width (CoglDmaBufHandle *dmabuf_handle)
209 {
210   return dmabuf_handle->width;
211 }
212 
213 int
cogl_dma_buf_handle_get_height(CoglDmaBufHandle * dmabuf_handle)214 cogl_dma_buf_handle_get_height (CoglDmaBufHandle *dmabuf_handle)
215 {
216   return dmabuf_handle->height;
217 }
218 
219 int
cogl_dma_buf_handle_get_stride(CoglDmaBufHandle * dmabuf_handle)220 cogl_dma_buf_handle_get_stride (CoglDmaBufHandle *dmabuf_handle)
221 {
222   return dmabuf_handle->stride;
223 }
224 
225 int
cogl_dma_buf_handle_get_offset(CoglDmaBufHandle * dmabuf_handle)226 cogl_dma_buf_handle_get_offset (CoglDmaBufHandle *dmabuf_handle)
227 {
228   return dmabuf_handle->offset;
229 }
230 
231 int
cogl_dma_buf_handle_get_bpp(CoglDmaBufHandle * dmabuf_handle)232 cogl_dma_buf_handle_get_bpp (CoglDmaBufHandle *dmabuf_handle)
233 {
234   return dmabuf_handle->bpp;
235 }
236