1 /*
2 * Copyright © 2019 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26 #include "config.h"
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <assert.h>
30 #include <signal.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <libweston/libweston.h>
34 #include <libweston/weston-log.h>
35 #include "compositor/weston.h"
36 #include "weston-content-protection-server-protocol.h"
37 #include "shared/helpers.h"
38 #include "shared/timespec-util.h"
39
40 #define content_protection_log(cp, ...) \
41 weston_log_scope_printf((cp)->debug, __VA_ARGS__)
42
43 static const char * const content_type_name [] = {
44 [WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED] = "UNPROTECTED",
45 [WESTON_PROTECTED_SURFACE_TYPE_HDCP_0] = "TYPE-0",
46 [WESTON_PROTECTED_SURFACE_TYPE_HDCP_1] = "TYPE-1",
47 };
48
49 void
weston_protected_surface_send_event(struct protected_surface * psurface,enum weston_hdcp_protection protection)50 weston_protected_surface_send_event(struct protected_surface *psurface,
51 enum weston_hdcp_protection protection)
52 {
53 struct wl_resource *p_resource;
54 enum weston_protected_surface_type protection_type;
55 struct content_protection *cp;
56 struct wl_resource *surface_resource;
57
58 p_resource = psurface->protection_resource;
59 if (!p_resource)
60 return;
61 /* No event to be sent to client, in case of enforced mode */
62 if (psurface->surface->protection_mode == WESTON_SURFACE_PROTECTION_MODE_ENFORCED)
63 return;
64 protection_type = (enum weston_protected_surface_type) protection;
65 weston_protected_surface_send_status(p_resource, protection_type);
66
67 cp = psurface->cp_backptr;
68 surface_resource = psurface->surface->resource;
69 content_protection_log(cp, "wl_surface@%"PRIu32" Protection type set to %s\n",
70 wl_resource_get_id(surface_resource),
71 content_type_name[protection_type]);
72 }
73
74 static void
set_type(struct wl_client * client,struct wl_resource * resource,enum weston_protected_surface_type content_type)75 set_type(struct wl_client *client, struct wl_resource *resource,
76 enum weston_protected_surface_type content_type)
77 {
78 struct content_protection *cp;
79 struct protected_surface *psurface;
80 enum weston_hdcp_protection weston_cp;
81 struct wl_resource *surface_resource;
82
83 psurface = wl_resource_get_user_data(resource);
84 if (!psurface)
85 return;
86 cp = psurface->cp_backptr;
87 surface_resource = psurface->surface->resource;
88
89 if (content_type < WESTON_PROTECTED_SURFACE_TYPE_UNPROTECTED ||
90 content_type > WESTON_PROTECTED_SURFACE_TYPE_HDCP_1) {
91 wl_resource_post_error(resource,
92 WESTON_PROTECTED_SURFACE_ERROR_INVALID_TYPE,
93 "wl_surface@%"PRIu32" Invalid content-type %d for request:set_type\n",
94 wl_resource_get_id(surface_resource), content_type);
95
96 content_protection_log(cp, "wl_surface@%"PRIu32" Invalid content-type %d for resquest:set_type\n",
97 wl_resource_get_id(surface_resource), content_type);
98 return;
99 }
100
101 content_protection_log(cp, "wl_surface@%"PRIu32" Request: Enable Content-Protection Type: %s\n",
102 wl_resource_get_id(surface_resource),
103 content_type_name[content_type]);
104
105 weston_cp = (enum weston_hdcp_protection) content_type;
106 psurface->surface->pending.desired_protection = weston_cp;
107 }
108
109 static void
protected_surface_destroy(struct wl_client * client,struct wl_resource * resource)110 protected_surface_destroy(struct wl_client *client, struct wl_resource *resource)
111 {
112 struct protected_surface *psurface;
113
114 psurface = wl_resource_get_user_data(resource);
115 if (!psurface)
116 return;
117 psurface->surface->pending.desired_protection = WESTON_HDCP_DISABLE;
118 }
119
120 static void
set_enforce_mode(struct wl_client * client,struct wl_resource * resource)121 set_enforce_mode(struct wl_client *client, struct wl_resource *resource)
122 {
123 /*
124 * Enforce Censored-Visibility. Compositor censors the protected
125 * surface on an unsecured output.
126 * In case of a surface, being shown on an unprotected output, the
127 * compositor hides the surface, not allowing it to be displayed on
128 * the unprotected output, without bothering the client. No difference
129 * for the protected outputs.
130 *
131 * The member 'protection_mode' is "double-buffered", so setting it in
132 * pending_state will cause the setting of the corresponding
133 * 'protection_mode' in weston_surface, after the commit.
134 *
135 * This function sets the 'protection_mode' of the weston_surface_state
136 * to 'enfoced'. The renderers inspect the flag and compare the
137 * desired_protection of the surface, to the current_protection of the
138 * output, based on that the real surface or a place-holder content,
139 * (e.g. solid color) are shown.
140 */
141
142 struct protected_surface *psurface;
143
144 psurface = wl_resource_get_user_data(resource);
145 if (!psurface)
146 return;
147
148 psurface->surface->pending.protection_mode =
149 WESTON_SURFACE_PROTECTION_MODE_ENFORCED;
150 }
151
152 static void
set_relax_mode(struct wl_client * client,struct wl_resource * resource)153 set_relax_mode(struct wl_client *client, struct wl_resource *resource)
154 {
155 /*
156 * Relaxed mode. By default this mode will be activated.
157 * In case of a surface, being shown in unprotected output,
158 * compositor just sends the event for protection status changed.
159 *
160 * On setting the relaxed mode, the 'protection_mode' member is queued
161 * to be set to 'relax' from the existing 'enforce' mode.
162 */
163
164 struct protected_surface *psurface;
165
166 psurface = wl_resource_get_user_data(resource);
167 if (!psurface)
168 return;
169
170 psurface->surface->pending.protection_mode =
171 WESTON_SURFACE_PROTECTION_MODE_RELAXED;
172 }
173
174 static const struct weston_protected_surface_interface protected_surface_implementation = {
175 protected_surface_destroy,
176 set_type,
177 set_enforce_mode,
178 set_relax_mode,
179 };
180
181 static void
cp_destroy_listener(struct wl_listener * listener,void * data)182 cp_destroy_listener(struct wl_listener *listener, void *data)
183 {
184 struct content_protection *cp;
185
186 cp = container_of(listener, struct content_protection,
187 destroy_listener);
188 wl_list_remove(&cp->destroy_listener.link);
189 wl_list_remove(&cp->protected_list);
190 weston_compositor_log_scope_destroy(cp->debug);
191 cp->debug = NULL;
192 cp->surface_protection_update = NULL;
193 free(cp);
194 }
195
196 static void
free_protected_surface(struct protected_surface * psurface)197 free_protected_surface(struct protected_surface *psurface)
198 {
199 psurface->surface->pending.desired_protection = WESTON_HDCP_DISABLE;
200 wl_resource_set_user_data(psurface->protection_resource, NULL);
201 wl_list_remove(&psurface->surface_destroy_listener.link);
202 wl_list_remove(&psurface->link);
203 free(psurface);
204 }
205
206 static void
surface_destroyed(struct wl_listener * listener,void * data)207 surface_destroyed(struct wl_listener *listener, void *data)
208 {
209 struct protected_surface *psurface;
210
211 psurface = container_of(listener, struct protected_surface,
212 surface_destroy_listener);
213 free_protected_surface(psurface);
214 }
215
216 static void
destroy_protected_surface(struct wl_resource * resource)217 destroy_protected_surface(struct wl_resource *resource)
218 {
219 struct protected_surface *psurface;
220
221 psurface = wl_resource_get_user_data(resource);
222 if (!psurface)
223 return;
224 free_protected_surface(psurface);
225 }
226
227 static void
get_protection(struct wl_client * client,struct wl_resource * cp_resource,uint32_t id,struct wl_resource * surface_resource)228 get_protection(struct wl_client *client, struct wl_resource *cp_resource,
229 uint32_t id, struct wl_resource *surface_resource)
230 {
231 struct wl_resource *resource;
232 struct weston_surface *surface;
233 struct content_protection *cp;
234 struct protected_surface *psurface;
235 struct wl_listener *listener;
236
237 surface = wl_resource_get_user_data(surface_resource);
238 assert(surface);
239 cp = wl_resource_get_user_data(cp_resource);
240 assert(cp);
241
242 /*
243 * Check if this client has a corresponding protected-surface
244 */
245
246 listener = wl_resource_get_destroy_listener(surface->resource,
247 surface_destroyed);
248
249 if (listener) {
250 wl_resource_post_error(cp_resource,
251 WESTON_CONTENT_PROTECTION_ERROR_SURFACE_EXISTS,
252 "wl_surface@%"PRIu32" Protection already exists",
253 wl_resource_get_id(surface_resource));
254 return;
255 }
256
257 psurface = zalloc(sizeof(struct protected_surface));
258 if (!psurface) {
259 wl_client_post_no_memory(client);
260 return;
261 }
262 psurface->cp_backptr = cp;
263 resource = wl_resource_create(client, &weston_protected_surface_interface,
264 1, id);
265 if (!resource) {
266 free(psurface);
267 wl_client_post_no_memory(client);
268 return;
269 }
270
271 wl_list_insert(&cp->protected_list, &psurface->link);
272 wl_resource_set_implementation(resource, &protected_surface_implementation,
273 psurface,
274 destroy_protected_surface);
275
276 psurface->protection_resource = resource;
277 psurface->surface = surface;
278 psurface->surface_destroy_listener.notify = surface_destroyed;
279 wl_resource_add_destroy_listener(surface->resource,
280 &psurface->surface_destroy_listener);
281 weston_protected_surface_send_event(psurface,
282 psurface->surface->current_protection);
283 }
284
285 static void
destroy_protection(struct wl_client * client,struct wl_resource * cp_resource)286 destroy_protection(struct wl_client *client, struct wl_resource *cp_resource)
287 {
288 wl_resource_destroy(cp_resource);
289 }
290
291 static const
292 struct weston_content_protection_interface content_protection_implementation = {
293 destroy_protection,
294 get_protection,
295 };
296
297 static void
bind_weston_content_protection(struct wl_client * client,void * data,uint32_t version,uint32_t id)298 bind_weston_content_protection(struct wl_client *client, void *data,
299 uint32_t version, uint32_t id)
300 {
301 struct content_protection *cp = data;
302 struct wl_resource *resource;
303
304 resource = wl_resource_create(client,
305 &weston_content_protection_interface,
306 1, id);
307 if (!resource) {
308 wl_client_post_no_memory(client);
309 return;
310 }
311
312 wl_resource_set_implementation(resource,
313 &content_protection_implementation,
314 cp, NULL);
315 }
316 /* Advertise the content-protection support.
317 *
318 * Calling this function sets up the content-protection support via HDCP.
319 * This exposes the global interface, visible to the client, enabling them to
320 * request for content-protection for their surfaces according to the type of
321 * content.
322 */
323
324 WL_EXPORT int
weston_compositor_enable_content_protection(struct weston_compositor * compositor)325 weston_compositor_enable_content_protection(struct weston_compositor *compositor)
326 {
327 struct content_protection *cp;
328
329 cp = zalloc(sizeof(*cp));
330 if (cp == NULL)
331 return -1;
332 cp->compositor = compositor;
333
334 compositor->content_protection = cp;
335 wl_list_init(&cp->protected_list);
336 if (wl_global_create(compositor->wl_display,
337 &weston_content_protection_interface, 1, cp,
338 bind_weston_content_protection) == NULL)
339 return -1;
340
341 cp->destroy_listener.notify = cp_destroy_listener;
342 wl_signal_add(&compositor->destroy_signal, &cp->destroy_listener);
343 cp->debug = weston_compositor_add_log_scope(compositor->weston_log_ctx,
344 "content-protection-debug",
345 "debug-logs for content-protection",
346 NULL, NULL);
347 return 0;
348 }
349