1 /*
2  * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "wayland-eglstream.h"
24 #include "wayland-eglstream-server.h"
25 #include "wayland-thread.h"
26 #include "wayland-eglhandle.h"
27 #include "wayland-egldisplay.h"
28 #include "wayland-eglutils.h"
29 #include "wayland-egl-ext.h"
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <assert.h>
33 
34 #define WL_EGL_CONN_WAIT_USECS    1e3 /* 1 msec */
35 #define WL_EGL_CONN_TIMEOUT_USECS 1e6 /* 1 sec */
36 
wlEglCreateStreamAttribHook(EGLDisplay dpy,const EGLAttrib * attribs)37 EGLStreamKHR wlEglCreateStreamAttribHook(EGLDisplay dpy,
38                                          const EGLAttrib *attribs)
39 {
40     WlEglPlatformData           *data        = NULL;
41     EGLStreamKHR                 stream      = EGL_NO_STREAM_KHR;
42     struct wl_eglstream_display *wlStreamDpy = NULL;
43     struct wl_resource          *resource    = NULL;
44     struct wl_eglstream         *wlStream    = NULL;
45     int                          nAttribs    = 0;
46     int                          idx         = 0;
47     int                          fd          = -1;
48     EGLint                       err         = EGL_SUCCESS;
49 
50     /* Parse attribute list and count internal attributes */
51     if (attribs) {
52         while (attribs[idx] != EGL_NONE) {
53             if (attribs[idx] == EGL_WAYLAND_EGLSTREAM_WL) {
54                 if (resource != NULL) {
55                     err =  EGL_BAD_MATCH;
56                     break;
57                 }
58 
59                 resource = (struct wl_resource *)attribs[idx + 1];
60                 if (resource == NULL) {
61                     err = EGL_BAD_ACCESS;
62                     break;
63                 }
64             } else {
65                 /* Internal attribute */
66                 nAttribs++;
67             }
68             idx += 2;
69         }
70     }
71 
72     if ((err == EGL_SUCCESS) && (resource == NULL)) {
73         /* No EGL_WAYLAND_EGLSTREAM_WL attribute provided, which means dpy is
74          * external. Forward this call to the underlying driver as there's
75          * nothing to do here */
76         WlEglDisplay *display = (WlEglDisplay *)dpy;
77         return display->data->egl.createStreamAttrib(display->devDpy->eglDisplay,
78                                                      attribs);
79     }
80 
81     /* Otherwise, we must create a server-side stream */
82     wlExternalApiLock();
83 
84     wlStreamDpy = wl_eglstream_display_get(dpy);
85     if (wlStreamDpy == NULL) {
86         err = EGL_BAD_ACCESS;
87     } else {
88         data = wlStreamDpy->data;
89     }
90 
91     if (err != EGL_SUCCESS) {
92         goto fail;
93     }
94 
95     wlStream = wl_eglstream_display_get_stream(wlStreamDpy, resource);
96     if (wlStream == NULL) {
97         err = EGL_BAD_ACCESS;
98         goto fail;
99     }
100 
101     if (wlStream->eglStream != EGL_NO_STREAM_KHR ||
102         wlStream->handle == -1) {
103         err = EGL_BAD_STREAM_KHR;
104         goto fail;
105     }
106 
107     if (wlStream->fromFd) {
108         /* Check for EGL_KHR_stream_cross_process_fd support */
109         if (!wlStreamDpy->exts.stream_cross_process_fd) {
110             err = EGL_BAD_ACCESS;
111             goto fail;
112         }
113 
114         /* eglCreateStreamFromFileDescriptorKHR from
115          * EGL_KHR_stream_cross_process_fd does not take attributes. Thus, only
116          * EGL_WAYLAND_EGLSTREAM_WL should have been specified and processed
117          * above. caps_override is an exception to this, since the wayland
118          * compositor calling into this function wouldn't be aware of an
119          * override in place */
120         if (nAttribs != 0 && !wlStreamDpy->caps_override) {
121             err = EGL_BAD_ATTRIBUTE;
122             goto fail;
123         }
124 
125         fd = wlStream->handle;
126         stream = data->egl.createStreamFromFD(dpy, wlStream->handle);
127 
128         /* Clean up */
129         close(fd);
130         wlStream->handle = -1;
131     }
132 #if defined(EGL_NV_stream_attrib) && \
133     defined(EGL_NV_stream_remote) && \
134     defined(EGL_NV_stream_socket)
135     else {
136         EGLAttrib *attribs2 = NULL;
137 
138         /* Check for required extensions support */
139         if (!wlStreamDpy->exts.stream_attrib ||
140             !wlStreamDpy->exts.stream_remote ||
141             !wlStreamDpy->exts.stream_socket ||
142             (!wlStreamDpy->exts.stream_socket_inet &&
143              !wlStreamDpy->exts.stream_socket_unix)) {
144             err = EGL_BAD_ACCESS;
145             goto fail;
146         }
147 
148         /* If not inet connection supported, wlStream should not be inet */
149         if (!wlStreamDpy->exts.stream_socket_inet &&
150             wlStream->isInet) {
151             err = EGL_BAD_ACCESS;
152             goto fail;
153         }
154 
155         /* Create attributes array to pass down to the actual EGL stream
156          * creation function */
157         attribs2 = (EGLAttrib *)malloc((2*(nAttribs + 5) + 1)*sizeof(*attribs2));
158 
159         nAttribs = 0;
160         attribs2[nAttribs++] = EGL_STREAM_TYPE_NV;
161         attribs2[nAttribs++] = EGL_STREAM_CROSS_PROCESS_NV;
162         attribs2[nAttribs++] = EGL_STREAM_PROTOCOL_NV;
163         attribs2[nAttribs++] = EGL_STREAM_PROTOCOL_SOCKET_NV;
164         attribs2[nAttribs++] = EGL_STREAM_ENDPOINT_NV;
165         attribs2[nAttribs++] = EGL_STREAM_CONSUMER_NV;
166         attribs2[nAttribs++] = EGL_SOCKET_TYPE_NV;
167         attribs2[nAttribs++] = (wlStream->isInet ? EGL_SOCKET_TYPE_INET_NV :
168                                                    EGL_SOCKET_TYPE_UNIX_NV);
169         attribs2[nAttribs++] = EGL_SOCKET_HANDLE_NV;
170         attribs2[nAttribs++] = (EGLAttrib)wlStream->handle;
171 
172         /* Include internal attributes given by the application */
173         while (attribs && attribs[0] != EGL_NONE) {
174             switch (attribs[0]) {
175                 /* Filter out external attributes */
176                 case EGL_WAYLAND_EGLSTREAM_WL:
177                     break;
178 
179                 /* EGL_NV_stream_remote attributes shouldn't be set by the
180                  * application */
181                 case EGL_STREAM_TYPE_NV:
182                 case EGL_STREAM_PROTOCOL_NV:
183                 case EGL_STREAM_ENDPOINT_NV:
184                 case EGL_SOCKET_TYPE_NV:
185                 case EGL_SOCKET_HANDLE_NV:
186                     free(attribs2);
187                     err = EGL_BAD_ATTRIBUTE;
188                     goto fail;
189 
190                 /* Everything else is fine and will be handled by EGL */
191                 default:
192                     attribs2[nAttribs++] = attribs[0];
193                     attribs2[nAttribs++] = attribs[1];
194             }
195 
196             attribs += 2;
197         }
198         attribs2[nAttribs] = EGL_NONE;
199 
200         stream = data->egl.createStreamAttrib(dpy, attribs2);
201 
202         /* Clean up */
203         free(attribs2);
204 
205         if (stream != EGL_NO_STREAM_KHR) {
206             /* Wait for the stream to establish connection with the producer's
207              * side */
208             uint32_t timeout = WL_EGL_CONN_TIMEOUT_USECS;
209             EGLint state = EGL_STREAM_STATE_INITIALIZING_NV;
210 
211             do {
212                 usleep(WL_EGL_CONN_WAIT_USECS);
213                 timeout -= WL_EGL_CONN_WAIT_USECS;
214 
215                 if (!data->egl.queryStream(dpy,
216                                            stream,
217                                            EGL_STREAM_STATE_KHR,
218                                            &state)) {
219                     break;
220                 }
221             } while ((state == EGL_STREAM_STATE_INITIALIZING_NV) &&
222                      (timeout > 0));
223 
224             if (state == EGL_STREAM_STATE_INITIALIZING_NV) {
225                 data->egl.destroyStream(dpy, stream);
226                 stream = EGL_NO_STREAM_KHR;
227             }
228         }
229     }
230 #endif
231 
232     if (stream == EGL_NO_STREAM_KHR) {
233         err = EGL_BAD_ACCESS;
234         goto fail;
235     }
236 
237     wlStream->eglStream = stream;
238     wlStream->handle = -1;
239 
240     wlExternalApiUnlock();
241 
242     return stream;
243 
244 fail:
245     wlExternalApiUnlock();
246     wlEglSetError(data, err);
247     return EGL_NO_STREAM_KHR;
248 }
249 
250