1 /**************************************************************************
5 Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
7 Contact: Eunchul Kim <chulspro.kim@samsung.com>,
8 JinYoung Jeon <jy0.jeon@samsung.com>,
9 Taeheon Kim <th908.kim@samsung.com>,
10 YoungJun Cho <yj44.cho@samsung.com>,
11 SooChan Lim <sc1.lim@samsung.com>,
12 Boram Park <sc1.lim@samsung.com>
14 Permission is hereby granted, free of charge, to any person obtaining a
15 copy of this software and associated documentation files (the
16 "Software"), to deal in the Software without restriction, including
17 without limitation the rights to use, copy, modify, merge, publish,
18 distribute, sub license, and/or sell copies of the Software, and to
19 permit persons to whom the Software is furnished to do so, subject to
20 the following conditions:
22 The above copyright notice and this permission notice (including the
23 next paragraph) shall be included in all copies or substantial portions
26 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
29 IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
30 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
31 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
32 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 **************************************************************************/
41 #include "tdm_private.h"
43 #include "tdm-server-protocol.h"
46 * - tdm server doesn't care about thread things.
47 * - DO NOT use the TDM internal functions here.
48 * However, the internal function which does lock/unlock the mutex of
49 * private_display in itself can be called.
50 * - DO NOT use the tdm_private_display structure here.
53 struct _tdm_private_server {
54 tdm_private_loop *private_loop;
55 struct list_head client_list;
56 struct list_head vblank_list;
59 typedef struct _tdm_server_client_info {
60 struct list_head link;
61 tdm_private_server *private_server;
62 struct wl_resource *resource;
64 tdm_output *vblank_output;
66 unsigned int last_tv_sec;
67 unsigned int last_tv_usec;
68 } tdm_server_client_info;
70 typedef struct _tdm_server_vblank_info {
71 struct list_head link;
72 tdm_server_client_info *client_info;
73 struct wl_resource *resource;
76 unsigned int req_usec;
78 tdm_event_loop_source *timer_source;
79 unsigned int timer_target_sec;
80 unsigned int timer_target_usec;
81 } tdm_server_vblank_info;
83 static tdm_private_server *keep_private_server;
84 static int tdm_debug_server;
87 _tdm_server_send_done(tdm_server_vblank_info *vblank_info, unsigned int sequence,
88 unsigned int tv_sec, unsigned int tv_usec);
91 _tdm_server_cb_timer(void *user_data)
93 tdm_server_vblank_info *vblank_info = (tdm_server_vblank_info*)user_data;
95 _tdm_server_send_done(vblank_info, 0,
96 vblank_info->timer_target_sec,
97 vblank_info->timer_target_usec);
99 return TDM_ERROR_NONE;
103 _tdm_server_update_timer(tdm_server_vblank_info *vblank_info, int interval)
105 tdm_server_client_info *client_info = vblank_info->client_info;
106 tdm_private_server *private_server = client_info->private_server;
107 tdm_private_loop *private_loop = private_server->private_loop;
108 unsigned long last, prev_req, req, curr, next;
112 ret = tdm_display_lock(private_loop->dpy);
113 TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret);
115 if (!vblank_info->timer_source) {
116 vblank_info->timer_source =
117 tdm_event_loop_add_timer_handler(private_loop->dpy,
118 _tdm_server_cb_timer,
121 if (!vblank_info->timer_source) {
122 TDM_ERR("couldn't add timer");
123 tdm_display_unlock(private_loop->dpy);
124 return TDM_ERROR_OPERATION_FAILED;
128 last = (unsigned long)client_info->last_tv_sec * 1000000 + client_info->last_tv_usec;
129 req = (unsigned long)vblank_info->req_sec * 1000000 + vblank_info->req_usec;
130 curr = tdm_helper_get_time_in_micros();
132 prev_req = last + (unsigned int)((req - last) / client_info->vblank_gap) * client_info->vblank_gap;
133 next = prev_req + (unsigned long)(client_info->vblank_gap * interval);
136 next += (unsigned long)client_info->vblank_gap;
138 TDM_DBG("last(%.6lu) req(%.6lu) curr(%.6lu) prev_req(%.6lu) next(%.6lu)",
139 last, req, curr, prev_req, next);
141 ms_delay = (int)ceil((double)(next - curr) / 1000);
145 TDM_DBG("delay(%lu) ms_delay(%d)", next - curr, ms_delay);
147 ret = tdm_event_loop_source_timer_update(vblank_info->timer_source, ms_delay);
148 if (ret != TDM_ERROR_NONE) {
149 tdm_event_loop_source_remove(vblank_info->timer_source);
150 vblank_info->timer_source = NULL;
151 tdm_display_unlock(private_loop->dpy);
152 TDM_ERR("couldn't update timer");
153 return TDM_ERROR_OPERATION_FAILED;
156 TDM_DBG("timer tick: %d us", (int)(next - last));
158 vblank_info->timer_target_sec = next / 1000000;
159 vblank_info->timer_target_usec = next % 1000000;
161 tdm_display_unlock(private_loop->dpy);
163 return TDM_ERROR_NONE;
167 _tdm_server_send_done(tdm_server_vblank_info *vblank_info, unsigned int sequence,
168 unsigned int tv_sec, unsigned int tv_usec)
170 tdm_server_vblank_info *found;
171 tdm_server_client_info *client_info;
172 unsigned long vtime = (tv_sec * 1000000) + tv_usec;
173 unsigned long curr = tdm_helper_get_time_in_micros();
175 if (!keep_private_server)
178 LIST_FIND_ITEM(vblank_info, &keep_private_server->vblank_list,
179 tdm_server_vblank_info, link, found);
181 TDM_DBG("vblank_info(%p) is destroyed", vblank_info);
185 client_info = vblank_info->client_info;
186 client_info->last_tv_sec = tv_sec;
187 client_info->last_tv_usec = tv_usec;
189 TDM_DBG("wl_tdm_vblank@%d done. tv(%lu) curr(%lu)",
190 wl_resource_get_id(vblank_info->resource), vtime, curr);
192 if (tdm_debug_server) {
193 if (curr - vtime > 1000) /* 1ms */
194 TDM_WRN("delay: %d us", (int)(curr - vtime));
197 wl_tdm_vblank_send_done(vblank_info->resource, sequence, tv_sec, tv_usec);
198 wl_resource_destroy(vblank_info->resource);
202 _tdm_server_cb_output_vblank(tdm_output *output, unsigned int sequence,
203 unsigned int tv_sec, unsigned int tv_usec,
206 tdm_server_vblank_info *vblank_info = (tdm_server_vblank_info*)user_data;
208 _tdm_server_send_done(vblank_info, sequence, tv_sec, tv_usec);
212 destroy_vblank_callback(struct wl_resource *resource)
214 tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource);
216 if (vblank_info->timer_source) {
217 tdm_private_server *private_server = vblank_info->client_info->private_server;
219 tdm_display_lock(private_server->private_loop->dpy);
220 tdm_event_loop_source_remove(vblank_info->timer_source);
221 tdm_display_unlock(private_server->private_loop->dpy);
224 LIST_DEL(&vblank_info->link);
229 _tdm_server_client_cb_destroy(struct wl_client *client, struct wl_resource *resource)
231 wl_resource_destroy(resource);
235 _tdm_server_client_cb_wait_vblank(struct wl_client *client,
236 struct wl_resource *resource,
237 uint32_t id, const char *name,
238 int32_t sw_timer, int32_t interval,
239 uint32_t req_sec, uint32_t req_usec)
241 tdm_server_client_info *client_info = wl_resource_get_user_data(resource);
242 tdm_private_server *private_server = client_info->private_server;
243 tdm_private_loop *private_loop = private_server->private_loop;
244 tdm_server_vblank_info *vblank_info;
245 struct wl_resource *vblank_resource;
246 tdm_output *found = NULL;
247 tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_ON;
251 TDM_DBG("The tdm client requests vblank");
253 if (tdm_debug_server) {
254 unsigned long curr = tdm_helper_get_time_in_micros();
255 unsigned long reqtime = (req_sec * 1000000) + req_usec;
256 if (curr - reqtime > 1000) /* 1ms */
257 TDM_WRN("delay(req): %d us", (int)(curr - reqtime));
260 if (client_info->vblank_output) {
262 ret = tdm_output_get_model_info(client_info->vblank_output, NULL, &model, NULL);
263 if (model && !strncmp(model, name, TDM_NAME_LEN))
264 found = client_info->vblank_output;
270 tdm_display_get_output_count(private_loop->dpy, &count);
272 for (i = 0; i < count; i++) {
273 tdm_output *output= tdm_display_get_output(private_loop->dpy, i, NULL);
274 tdm_output_conn_status status;
276 ret = tdm_output_get_conn_status(output, &status);
277 if (ret || status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
281 ret = tdm_output_get_model_info(output, NULL, &model, NULL);
285 if (strncmp(model, name, TDM_NAME_LEN))
294 wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_INVALID_NAME,
295 "There is no '%s' output", name);
296 TDM_ERR("There is no '%s' output", name);
300 if (client_info->vblank_output != found) {
301 const tdm_output_mode *mode = NULL;
303 client_info->vblank_output = found;
305 tdm_output_get_mode(client_info->vblank_output, &mode);
306 if (!mode || mode->vrefresh <= 0) {
307 wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED,
308 "couldn't get mode of %s", name);
309 TDM_ERR("couldn't get mode of %s", name);
313 client_info->vblank_gap = (double)1000000 / mode->vrefresh;
314 TDM_INFO("vblank_gap(%.6lf)", client_info->vblank_gap);
317 tdm_output_get_dpms(found, &dpms_value);
319 if (dpms_value != TDM_OUTPUT_DPMS_ON && !sw_timer) {
320 wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_DPMS_OFF,
321 "dpms '%s'", tdm_get_dpms_str(dpms_value));
322 TDM_ERR("dpms '%s'", tdm_get_dpms_str(dpms_value));
326 vblank_info = calloc(1, sizeof *vblank_info);
328 wl_resource_post_no_memory(resource);
329 TDM_ERR("alloc failed");
333 TDM_DBG("wl_tdm_vblank@%d output(%s) interval(%d)", id, name, interval);
336 wl_resource_create(client, &wl_tdm_vblank_interface,
337 wl_resource_get_version(resource), id);
338 if (!vblank_resource) {
339 wl_resource_post_no_memory(resource);
340 TDM_ERR("wl_resource_create failed");
344 vblank_info->resource = vblank_resource;
345 vblank_info->client_info = client_info;
346 vblank_info->req_sec = req_sec;
347 vblank_info->req_usec = req_usec;
349 if (dpms_value == TDM_OUTPUT_DPMS_ON) {
350 ret = tdm_output_wait_vblank(found, interval, 0,
351 _tdm_server_cb_output_vblank, vblank_info);
352 if (ret != TDM_ERROR_NONE) {
353 wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED,
354 "couldn't wait vblank for %s", name);
355 TDM_ERR("couldn't wait vblank for %s", name);
356 goto destroy_resource;
358 } else if (sw_timer) {
359 ret = _tdm_server_update_timer(vblank_info, interval);
360 if (ret != TDM_ERROR_NONE) {
361 wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED,
362 "couldn't update timer for %s", name);
363 TDM_ERR("couldn't update timer for %s", name);
364 goto destroy_resource;
367 wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED,
368 "bad implementation");
369 TDM_NEVER_GET_HERE();
370 goto destroy_resource;
373 wl_resource_set_implementation(vblank_resource, NULL, vblank_info,
374 destroy_vblank_callback);
376 LIST_ADDTAIL(&vblank_info->link, &private_server->vblank_list);
379 wl_resource_destroy(vblank_resource);
384 static const struct wl_tdm_client_interface tdm_client_implementation = {
385 _tdm_server_client_cb_destroy,
386 _tdm_server_client_cb_wait_vblank,
390 destroy_client_callback(struct wl_resource *resource)
392 tdm_server_client_info *client_info = wl_resource_get_user_data(resource);
393 LIST_DEL(&client_info->link);
398 _tdm_server_cb_create_client(struct wl_client *client,
399 struct wl_resource *resource, uint32_t id)
401 tdm_private_server *private_server = wl_resource_get_user_data(resource);
402 tdm_server_client_info *client_info;
403 struct wl_resource *client_resource;
405 client_info = calloc(1, sizeof *client_info);
407 wl_resource_post_no_memory(resource);
408 TDM_ERR("alloc failed");
413 wl_resource_create(client, &wl_tdm_client_interface,
414 wl_resource_get_version(resource), id);
415 if (!client_resource) {
416 wl_resource_post_no_memory(resource);
418 TDM_ERR("wl_resource_create failed");
422 client_info->private_server = private_server;
423 client_info->resource = client_resource;
425 wl_resource_set_implementation(client_resource, &tdm_client_implementation,
426 client_info, destroy_client_callback);
428 LIST_ADDTAIL(&client_info->link, &private_server->client_list);
431 static const struct wl_tdm_interface tdm_implementation = {
432 _tdm_server_cb_create_client,
436 _tdm_server_bind(struct wl_client *client, void *data,
437 uint32_t version, uint32_t id)
439 struct wl_resource *resource;
441 resource = wl_resource_create(client, &wl_tdm_interface, version, id);
443 wl_client_post_no_memory(client);
447 TDM_DBG("tdm server binding");
449 wl_resource_set_implementation(resource, &tdm_implementation, data, NULL);
453 tdm_server_init(tdm_private_loop *private_loop)
455 tdm_private_server *private_server;
458 debug = getenv("TDM_DEBUG_SERVER");
459 if (debug && (strstr(debug, "1")))
460 tdm_debug_server = 1;
462 if (private_loop->private_server)
463 return TDM_ERROR_NONE;
465 TDM_RETURN_VAL_IF_FAIL(private_loop, TDM_ERROR_OPERATION_FAILED);
466 TDM_RETURN_VAL_IF_FAIL(private_loop->wl_display, TDM_ERROR_OPERATION_FAILED);
468 if(wl_display_add_socket(private_loop->wl_display, "tdm-socket")) {
469 TDM_ERR("createing a tdm-socket failed");
470 return TDM_ERROR_OPERATION_FAILED;
473 private_server = calloc(1, sizeof *private_server);
474 if (!private_server) {
475 TDM_ERR("alloc failed");
476 return TDM_ERROR_OUT_OF_MEMORY;
479 LIST_INITHEAD(&private_server->client_list);
480 LIST_INITHEAD(&private_server->vblank_list);
482 if (!wl_global_create(private_loop->wl_display, &wl_tdm_interface, 1,
483 private_server, _tdm_server_bind)) {
484 TDM_ERR("creating a global resource failed");
485 free(private_server);
486 return TDM_ERROR_OUT_OF_MEMORY;
489 private_server->private_loop = private_loop;
490 private_loop->private_server = private_server;
491 keep_private_server = private_server;
493 return TDM_ERROR_NONE;
497 tdm_server_deinit(tdm_private_loop *private_loop)
499 tdm_server_vblank_info *v = NULL, *vv = NULL;
500 tdm_server_client_info *c = NULL, *cc = NULL;
501 tdm_private_server *private_server;
503 if (!private_loop->private_server)
506 private_server = private_loop->private_server;
508 LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_server->vblank_list, link) {
509 wl_resource_destroy(v->resource);
512 LIST_FOR_EACH_ENTRY_SAFE(c, cc, &private_server->client_list, link) {
513 wl_resource_destroy(c->resource);
516 free(private_server);
517 private_loop->private_server = NULL;
518 keep_private_server = NULL;