From 187fc69a84a832e0d3b90c8883d83beb948216b3 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 27 Apr 2016 18:45:51 +0900 Subject: [PATCH 01/16] implement for sync fo tdm_client_wait_vblank Change-Id: I6dd440473571e8cf717a3c74b784a036ee009da4 --- client/tdm_client.c | 28 ++++++++++++++++---- client/tdm_client.h | 5 ++-- protocol/tdm.xml | 1 + src/tdm_display.c | 6 +++++ src/tdm_private.h | 3 +++ src/tdm_server.c | 69 +++++++++++++++++++++++++++++++++---------------- tools/tdm_test_client.c | 36 +++++++++++++++++--------- 7 files changed, 107 insertions(+), 41 deletions(-) diff --git a/client/tdm_client.c b/client/tdm_client.c index 9c34864..4a39714 100644 --- a/client/tdm_client.c +++ b/client/tdm_client.c @@ -65,6 +65,7 @@ typedef struct _tdm_client_vblank_info { unsigned int req_sec; unsigned int req_usec; void *user_data; + int need_free; } tdm_client_vblank_info; static void @@ -212,8 +213,12 @@ _tdm_client_cb_vblank_done(void *data, struct wl_tdm_vblank *vblank, vblank_info->func(sequence, tv_sec, tv_usec, vblank_info->user_data); } - LIST_DEL(&vblank_info->link); - free(vblank_info); + if (vblank_info->need_free) { + LIST_DEL(&vblank_info->link); + free(vblank_info); + } else { + vblank_info->need_free = 1; + } } static const struct wl_tdm_vblank_listener tdm_client_vblank_listener = { @@ -228,6 +233,7 @@ tdm_client_wait_vblank(tdm_client *client, char *name, tdm_private_client *private_client = (tdm_private_client*)client; tdm_client_vblank_info *vblank_info; struct timespec tp; + int ret = 0; TDM_RETURN_VAL_IF_FAIL(name != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_CLIENT_ERROR_INVALID_PARAMETER); @@ -245,6 +251,7 @@ tdm_client_wait_vblank(tdm_client *client, char *name, vblank_info->req_sec = (unsigned int)tp.tv_sec; vblank_info->req_usec = (unsigned int)(tp.tv_nsec/1000L); + vblank_info->need_free = (sync) ? 0 : 1; vblank_info->vblank = wl_tdm_wait_vblank(private_client->tdm, name, sw_timer, interval, @@ -265,10 +272,21 @@ tdm_client_wait_vblank(tdm_client *client, char *name, vblank_info->user_data = user_data; LIST_ADDTAIL(&vblank_info->link, &private_client->vblank_list); - if (sync) - wl_display_roundtrip(private_client->display); - else + if (!sync) { wl_display_flush(private_client->display); + return TDM_CLIENT_ERROR_NONE; + } + + while (ret != -1 && !vblank_info->need_free) + ret = wl_display_dispatch(private_client->display); + + clock_gettime(CLOCK_MONOTONIC, &tp); + TDM_DBG("block during %d us", + ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec/1000L)) + - (vblank_info->req_sec * 1000000 + vblank_info->req_usec)); + + LIST_DEL(&vblank_info->link); + free(vblank_info); return TDM_CLIENT_ERROR_NONE; } diff --git a/client/tdm_client.h b/client/tdm_client.h index bceecc1..06e2c9b 100644 --- a/client/tdm_client.h +++ b/client/tdm_client.h @@ -59,6 +59,7 @@ typedef enum TDM_CLIENT_ERROR_INVALID_PARAMETER = -2, /**< wrong input parameter */ TDM_CLIENT_ERROR_PERMISSION_DENIED = -3, /**< access denied */ TDM_CLIENT_ERROR_OUT_OF_MEMORY = -4, /**< no free memory */ + TDM_CLIENT_ERROR_DPMS_OFF = -5, /**< dpms off */ } tdm_client_error; /** @@ -142,8 +143,8 @@ tdm_client_handle_events(tdm_client *client); /** * @brief Wait for VBLANK * @details After interval vblanks, a client vblank handler will be called. - * If 'sw_timer' param is 1, TDM will use the SW timer and call a client vblank - * handler when DPMS off. + * If 'sw_timer' param is 1 in case of DPMS off, TDM will use the SW timer and + * call a client vblank handler. Otherwise, this function will return error. * @param[in] client A TDM client object * @param[in] name The name of a TDM output * @param[in] sw_timer 0: not using SW timer, 1: using SW timer diff --git a/protocol/tdm.xml b/protocol/tdm.xml index 29f741d..6d79462 100644 --- a/protocol/tdm.xml +++ b/protocol/tdm.xml @@ -17,6 +17,7 @@ + diff --git a/src/tdm_display.c b/src/tdm_display.c index 0d6e274..6d02fd9 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -136,6 +136,12 @@ struct type_name status_names[] = { INTERN type_name_fn(status) +INTERN const char* +tdm_get_dpms_str(tdm_output_dpms dpms_value) +{ + return dpms_str(dpms_value); +} + EXTERN tdm_error tdm_display_get_capabilities(tdm_display *dpy, tdm_display_capability *capabilities) diff --git a/src/tdm_private.h b/src/tdm_private.h index 8002366..fd7c368 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -303,6 +303,9 @@ typedef struct _tdm_buffer_info { struct list_head link; } tdm_buffer_info; +const char* +tdm_get_dpms_str(tdm_output_dpms dpms_value); + tdm_private_output * tdm_display_find_output_stamp(tdm_private_display *private_display, unsigned long stamp); diff --git a/src/tdm_server.c b/src/tdm_server.c index 69abc20..034a65c 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -110,11 +110,19 @@ _tdm_server_cb_wait_vblank(struct wl_client *client, tdm_server_vblank_info *vblank_info; struct wl_resource *vblank_resource; tdm_output *found = NULL; + tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_ON; int count = 0, i; tdm_error ret; TDM_DBG("The tdm client requests vblank"); + if (tdm_debug_server) { + unsigned long curr = tdm_helper_get_time_in_micros(); + unsigned long reqtime = (req_sec * 1000000) + req_usec; + if (curr - reqtime > 1000) /* 1ms */ + TDM_WRN("delay(req): %d us", (int)(curr - reqtime)); + } + tdm_display_get_output_count(private_loop->dpy, &count); for (i = 0; i < count; i++) { @@ -144,20 +152,17 @@ _tdm_server_cb_wait_vblank(struct wl_client *client, return; } - /* TODO: need to implement things related with sw_timer */ + tdm_output_get_dpms(found, &dpms_value); - vblank_resource = - wl_resource_create(client, &wl_tdm_vblank_interface, - wl_resource_get_version(resource), id); - if (!vblank_resource) { - wl_resource_post_no_memory(resource); - TDM_ERR("wl_resource_create failed"); + if (dpms_value != TDM_OUTPUT_DPMS_ON && !sw_timer) { + wl_resource_post_error(resource, WL_TDM_ERROR_DPMS_OFF, + "dpms '%s'", tdm_get_dpms_str(dpms_value)); + TDM_ERR("dpms '%s'", tdm_get_dpms_str(dpms_value)); return; } vblank_info = calloc(1, sizeof *vblank_info); if (!vblank_info) { - wl_resource_destroy(vblank_resource); wl_resource_post_no_memory(resource); TDM_ERR("alloc failed"); return; @@ -165,22 +170,37 @@ _tdm_server_cb_wait_vblank(struct wl_client *client, TDM_DBG("wl_tdm_vblank@%d output(%s) interval(%d)", id, name, interval); - ret = tdm_output_wait_vblank(found, interval, 0, - _tdm_server_cb_output_vblank, vblank_info); - if (ret != TDM_ERROR_NONE) { - wl_resource_destroy(vblank_resource); - free(vblank_info); - wl_resource_post_error(resource, WL_TDM_ERROR_OPERATION_FAILED, - "couldn't wait vblank for %s", name); - TDM_ERR("couldn't wait vblank for %s", name); - return; + vblank_resource = + wl_resource_create(client, &wl_tdm_vblank_interface, + wl_resource_get_version(resource), id); + if (!vblank_resource) { + wl_resource_post_no_memory(resource); + TDM_ERR("wl_resource_create failed"); + goto free_info; } - if (tdm_debug_server) { - unsigned long curr = tdm_helper_get_time_in_micros(); - unsigned long reqtime = (req_sec * 1000000) + req_usec; - if (curr - reqtime > 1000) /* 1ms */ - TDM_WRN("delay(req): %d us", (int)(curr - reqtime)); + if (dpms_value == TDM_OUTPUT_DPMS_ON) { + ret = tdm_output_wait_vblank(found, interval, 0, + _tdm_server_cb_output_vblank, vblank_info); + if (ret != TDM_ERROR_NONE) { + wl_resource_post_error(resource, WL_TDM_ERROR_OPERATION_FAILED, + "couldn't wait vblank for %s", name); + TDM_ERR("couldn't wait vblank for %s", name); + goto destroy_resource; + } + } else if (sw_timer) { + /* TODO: need to implement things related with sw_timer */ + if (ret != TDM_ERROR_NONE) { + wl_resource_post_error(resource, WL_TDM_ERROR_OPERATION_FAILED, + "not implemented yet"); + TDM_ERR("not implemented yet"); + goto destroy_resource; + } + } else { + wl_resource_post_error(resource, WL_TDM_ERROR_OPERATION_FAILED, + "bad implementation"); + TDM_NEVER_GET_HERE(); + goto destroy_resource; } vblank_info->resource = vblank_resource; @@ -190,6 +210,11 @@ _tdm_server_cb_wait_vblank(struct wl_client *client, destroy_vblank_callback); LIST_ADDTAIL(&vblank_info->link, &private_server->vblank_list); + return; +destroy_resource: + wl_resource_destroy(vblank_resource); +free_info: + free(vblank_info); } static const struct wl_tdm_interface tdm_implementation = { diff --git a/tools/tdm_test_client.c b/tools/tdm_test_client.c index fcedae5..5e1f1bb 100644 --- a/tools/tdm_test_client.c +++ b/tools/tdm_test_client.c @@ -75,6 +75,16 @@ main(int argc, char *argv[]) tdm_client_error error; int fd = -1; struct pollfd fds; + int sync; + + if (argc < 2) { + printf("Usage: %s\n", argv[0]); + printf("\t%s 0 : non-sync\n", argv[0]); + printf("\t%s 1 : sync\n", argv[0]); + exit(1); + } + + sync = atoi(argv[1]); client = tdm_client_create(&error); if (error != TDM_CLIENT_ERROR_NONE) { @@ -95,26 +105,28 @@ main(int argc, char *argv[]) while (1) { int ret; - error = tdm_client_wait_vblank(client, "unknown-0", 1, 1, 0, + error = tdm_client_wait_vblank(client, "unknown-0", 1, 1, sync, _client_vblank_handler, NULL); if (error != TDM_CLIENT_ERROR_NONE) { printf("tdm_client_wait_vblank failed\n"); goto done; } - ret = poll(&fds, 1, -1); - if (ret < 0) { - if (errno == EBUSY) /* normal case */ - continue; - else { - printf("poll failed: %m\n"); - goto done; + if (!sync) { + ret = poll(&fds, 1, -1); + if (ret < 0) { + if (errno == EBUSY) /* normal case */ + continue; + else { + printf("poll failed: %m\n"); + goto done; + } } - } - error = tdm_client_handle_events(client); - if (error != TDM_CLIENT_ERROR_NONE) - printf("tdm_client_handle_events failed\n"); + error = tdm_client_handle_events(client); + if (error != TDM_CLIENT_ERROR_NONE) + printf("tdm_client_handle_events failed\n"); + } } done: -- 2.7.4 From 2d3906b8341b46fc6cf852316c99e3c3c3d164ab Mon Sep 17 00:00:00 2001 From: Boram Park Date: Thu, 28 Apr 2016 12:40:02 +0900 Subject: [PATCH 02/16] add wl_tdm_client interface To manage client's resources in tdm server side. Change-Id: I48cfa8f819f9447de188497892371be6a4113f02 --- client/tdm_client.c | 14 +++++--- protocol/tdm.xml | 11 +++++- src/tdm_server.c | 101 ++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 106 insertions(+), 20 deletions(-) diff --git a/client/tdm_client.c b/client/tdm_client.c index 4a39714..24a99eb 100644 --- a/client/tdm_client.c +++ b/client/tdm_client.c @@ -54,7 +54,7 @@ typedef struct _tdm_private_client { struct wl_display *display; struct wl_registry *registry; struct wl_tdm *tdm; - + struct wl_tdm_client *tdm_client; struct list_head vblank_list; } tdm_private_client; @@ -128,6 +128,9 @@ tdm_client_create(tdm_client_error *error) LIST_INITHEAD(&private_client->vblank_list); + private_client->tdm_client = wl_tdm_create_client(private_client->tdm); + TDM_GOTO_IF_FAIL(private_client->tdm_client != NULL, create_failed); + if (error) *error = TDM_CLIENT_ERROR_NONE; @@ -154,6 +157,8 @@ tdm_client_destroy(tdm_client *client) free(v); } + if (private_client->tdm_client) + wl_tdm_client_destroy(private_client->tdm_client); if (private_client->tdm) wl_tdm_destroy(private_client->tdm); if (private_client->registry) @@ -239,7 +244,7 @@ tdm_client_wait_vblank(tdm_client *client, char *name, TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_CLIENT_ERROR_INVALID_PARAMETER); TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); - TDM_RETURN_VAL_IF_FAIL(private_client->tdm != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); + TDM_RETURN_VAL_IF_FAIL(private_client->tdm_client != NULL, TDM_CLIENT_ERROR_INVALID_PARAMETER); vblank_info = calloc(1, sizeof *vblank_info); if (!vblank_info) { @@ -254,8 +259,9 @@ tdm_client_wait_vblank(tdm_client *client, char *name, vblank_info->need_free = (sync) ? 0 : 1; vblank_info->vblank = - wl_tdm_wait_vblank(private_client->tdm, name, sw_timer, interval, - vblank_info->req_sec, vblank_info->req_usec); + wl_tdm_client_wait_vblank(private_client->tdm_client, + name, sw_timer, interval, + vblank_info->req_sec, vblank_info->req_usec); if (!vblank_info->vblank) { TDM_ERR("couldn't create vblank resource"); free(vblank_info); diff --git a/protocol/tdm.xml b/protocol/tdm.xml index 6d79462..6fa7f3d 100644 --- a/protocol/tdm.xml +++ b/protocol/tdm.xml @@ -12,7 +12,7 @@ - + @@ -20,6 +20,7 @@ + @@ -31,4 +32,12 @@ + + + + + + + + diff --git a/src/tdm_server.c b/src/tdm_server.c index 034a65c..15d856e 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -48,13 +48,21 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ struct _tdm_private_server { + tdm_private_loop *private_loop; + struct list_head client_list; struct list_head vblank_list; }; +typedef struct _tdm_server_client_info { + struct list_head link; + tdm_private_server *private_server; + struct wl_resource *resource; +} tdm_server_client_info; + typedef struct _tdm_server_vblank_info { struct list_head link; + tdm_server_client_info *client_info; struct wl_resource *resource; - tdm_private_server *private_server; } tdm_server_vblank_info; static tdm_private_server *keep_private_server; @@ -100,13 +108,21 @@ destroy_vblank_callback(struct wl_resource *resource) } static void -_tdm_server_cb_wait_vblank(struct wl_client *client, - struct wl_resource *resource, - uint32_t id, const char *name, int32_t sw_timer, - int32_t interval, uint32_t req_sec, uint32_t req_usec) +_tdm_server_client_cb_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +_tdm_server_client_cb_wait_vblank(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, const char *name, + int32_t sw_timer, int32_t interval, + uint32_t req_sec, uint32_t req_usec) { - tdm_private_loop *private_loop = wl_resource_get_user_data(resource); - tdm_private_server *private_server = private_loop->private_server; + tdm_server_client_info *client_info = wl_resource_get_user_data(resource); + tdm_private_server *private_server = client_info->private_server; + tdm_private_loop *private_loop = private_server->private_loop; tdm_server_vblank_info *vblank_info; struct wl_resource *vblank_resource; tdm_output *found = NULL; @@ -146,7 +162,7 @@ _tdm_server_cb_wait_vblank(struct wl_client *client, } if (!found) { - wl_resource_post_error(resource, WL_TDM_ERROR_INVALID_NAME, + wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_INVALID_NAME, "There is no '%s' output", name); TDM_ERR("There is no '%s' output", name); return; @@ -155,7 +171,7 @@ _tdm_server_cb_wait_vblank(struct wl_client *client, tdm_output_get_dpms(found, &dpms_value); if (dpms_value != TDM_OUTPUT_DPMS_ON && !sw_timer) { - wl_resource_post_error(resource, WL_TDM_ERROR_DPMS_OFF, + wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_DPMS_OFF, "dpms '%s'", tdm_get_dpms_str(dpms_value)); TDM_ERR("dpms '%s'", tdm_get_dpms_str(dpms_value)); return; @@ -183,7 +199,7 @@ _tdm_server_cb_wait_vblank(struct wl_client *client, ret = tdm_output_wait_vblank(found, interval, 0, _tdm_server_cb_output_vblank, vblank_info); if (ret != TDM_ERROR_NONE) { - wl_resource_post_error(resource, WL_TDM_ERROR_OPERATION_FAILED, + wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED, "couldn't wait vblank for %s", name); TDM_ERR("couldn't wait vblank for %s", name); goto destroy_resource; @@ -191,20 +207,20 @@ _tdm_server_cb_wait_vblank(struct wl_client *client, } else if (sw_timer) { /* TODO: need to implement things related with sw_timer */ if (ret != TDM_ERROR_NONE) { - wl_resource_post_error(resource, WL_TDM_ERROR_OPERATION_FAILED, + wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED, "not implemented yet"); TDM_ERR("not implemented yet"); goto destroy_resource; } } else { - wl_resource_post_error(resource, WL_TDM_ERROR_OPERATION_FAILED, + wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED, "bad implementation"); TDM_NEVER_GET_HERE(); goto destroy_resource; } vblank_info->resource = vblank_resource; - vblank_info->private_server = private_server; + vblank_info->client_info = client_info; wl_resource_set_implementation(vblank_resource, NULL, vblank_info, destroy_vblank_callback); @@ -217,8 +233,56 @@ free_info: free(vblank_info); } +static const struct wl_tdm_client_interface tdm_client_implementation = { + _tdm_server_client_cb_destroy, + _tdm_server_client_cb_wait_vblank, +}; + +static void +destroy_client_callback(struct wl_resource *resource) +{ + tdm_server_client_info *client_info = wl_resource_get_user_data(resource); + + LIST_DEL(&client_info->link); + free(client_info); +} + +static void +_tdm_server_cb_create_client(struct wl_client *client, + struct wl_resource *resource, uint32_t id) +{ + tdm_private_server *private_server = wl_resource_get_user_data(resource); + tdm_server_client_info *client_info; + struct wl_resource *client_resource; + + client_info = calloc(1, sizeof *client_info); + if (!client_info) { + wl_resource_post_no_memory(resource); + TDM_ERR("alloc failed"); + return; + } + + client_resource = + wl_resource_create(client, &wl_tdm_client_interface, + wl_resource_get_version(resource), id); + if (!client_resource) { + wl_resource_post_no_memory(resource); + free(client_info); + TDM_ERR("wl_resource_create failed"); + return; + } + + client_info->private_server = private_server; + client_info->resource = client_resource; + + wl_resource_set_implementation(client_resource, &tdm_client_implementation, + client_info, destroy_client_callback); + + LIST_ADDTAIL(&client_info->link, &private_server->client_list); +} + static const struct wl_tdm_interface tdm_implementation = { - _tdm_server_cb_wait_vblank, + _tdm_server_cb_create_client, }; static void @@ -265,15 +329,17 @@ tdm_server_init(tdm_private_loop *private_loop) return TDM_ERROR_OUT_OF_MEMORY; } + LIST_INITHEAD(&private_server->client_list); LIST_INITHEAD(&private_server->vblank_list); if (!wl_global_create(private_loop->wl_display, &wl_tdm_interface, 1, - private_loop, _tdm_server_bind)) { + private_server, _tdm_server_bind)) { TDM_ERR("creating a global resource failed"); free(private_server); return TDM_ERROR_OUT_OF_MEMORY; } + private_server->private_loop = private_loop; private_loop->private_server = private_server; keep_private_server = private_server; @@ -284,6 +350,7 @@ INTERN void tdm_server_deinit(tdm_private_loop *private_loop) { tdm_server_vblank_info *v = NULL, *vv = NULL; + tdm_server_client_info *c = NULL, *cc = NULL; tdm_private_server *private_server; if (!private_loop->private_server) @@ -295,6 +362,10 @@ tdm_server_deinit(tdm_private_loop *private_loop) wl_resource_destroy(v->resource); } + LIST_FOR_EACH_ENTRY_SAFE(c, cc, &private_server->client_list, link) { + wl_resource_destroy(c->resource); + } + free(private_server); private_loop->private_server = NULL; keep_private_server = NULL; -- 2.7.4 From 2827248b3e727905a216ac1fc483aa1b72a82039 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Fri, 29 Apr 2016 11:25:42 +0900 Subject: [PATCH 03/16] using SW timer for TDM vblank when DPMS off Change-Id: I03159500bbe8e0b6a65b1cd82bf4ef6f1a015c68 --- client/tdm_client.c | 4 +- src/tdm_display.c | 41 +++++++++++ src/tdm_private.h | 8 ++ src/tdm_server.c | 207 ++++++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 228 insertions(+), 32 deletions(-) diff --git a/client/tdm_client.c b/client/tdm_client.c index 24a99eb..5797af8 100644 --- a/client/tdm_client.c +++ b/client/tdm_client.c @@ -255,7 +255,7 @@ tdm_client_wait_vblank(tdm_client *client, char *name, clock_gettime(CLOCK_MONOTONIC, &tp); vblank_info->req_sec = (unsigned int)tp.tv_sec; - vblank_info->req_usec = (unsigned int)(tp.tv_nsec/1000L); + vblank_info->req_usec = (unsigned int)(tp.tv_nsec/1000); vblank_info->need_free = (sync) ? 0 : 1; vblank_info->vblank = @@ -288,7 +288,7 @@ tdm_client_wait_vblank(tdm_client *client, char *name, clock_gettime(CLOCK_MONOTONIC, &tp); TDM_DBG("block during %d us", - ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec/1000L)) + ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec/1000)) - (vblank_info->req_sec * 1000000 + vblank_info->req_usec)); LIST_DEL(&vblank_info->link); diff --git a/src/tdm_display.c b/src/tdm_display.c index 6d02fd9..e8c6f60 100644 --- a/src/tdm_display.c +++ b/src/tdm_display.c @@ -142,6 +142,47 @@ tdm_get_dpms_str(tdm_output_dpms dpms_value) return dpms_str(dpms_value); } +INTERN tdm_error +_tdm_display_lock(tdm_display *dpy, const char *func) +{ + tdm_private_display *private_display = (tdm_private_display*)dpy; + int ret; + + if (tdm_debug_mutex) + TDM_INFO("mutex lock: %s", func); + + ret = pthread_mutex_trylock(&private_display->lock); + if (ret < 0) { + if (ret == EBUSY) { + TDM_ERR("mutex lock busy: %s", func); + } else { + TDM_ERR("mutex lock failed: %s(%m)", func); + } + return TDM_ERROR_OPERATION_FAILED; + } + + pthread_mutex_lock(&tdm_mutex_check_lock); + tdm_mutex_locked = 1; + pthread_mutex_unlock(&tdm_mutex_check_lock); + + return TDM_ERROR_NONE; +} + +INTERN void +_tdm_display_unlock(tdm_display *dpy, const char *func) +{ + tdm_private_display *private_display = (tdm_private_display*)dpy; + + if (tdm_debug_mutex) + TDM_INFO("mutex unlock: %s", func); + + pthread_mutex_lock(&tdm_mutex_check_lock); + tdm_mutex_locked = 0; + pthread_mutex_unlock(&tdm_mutex_check_lock); + + pthread_mutex_unlock(&private_display->lock); +} + EXTERN tdm_error tdm_display_get_capabilities(tdm_display *dpy, tdm_display_capability *capabilities) diff --git a/src/tdm_private.h b/src/tdm_private.h index fd7c368..671df8b 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -507,6 +507,14 @@ static inline int TDM_MUTEX_IS_LOCKED(void) return ret; } +tdm_error +_tdm_display_lock(tdm_display *dpy, const char *func); +void +_tdm_display_unlock(tdm_display *dpy, const char *func); + +#define tdm_display_lock(dpy) _tdm_display_lock(dpy, __FUNCTION__) +#define tdm_display_unlock(dpy) _tdm_display_unlock(dpy, __FUNCTION__) + #ifdef __cplusplus } #endif diff --git a/src/tdm_server.c b/src/tdm_server.c index 15d856e..41538ef 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -45,6 +45,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* CAUTION: * - tdm server doesn't care about thread things. * - DO NOT use the TDM internal functions here. + * However, the internal function which does lock/unlock the mutex of + * private_display in itself can be called. + * - DO NOT use the tdm_private_display structure here. */ struct _tdm_private_server { @@ -57,24 +60,117 @@ typedef struct _tdm_server_client_info { struct list_head link; tdm_private_server *private_server; struct wl_resource *resource; + + tdm_output *vblank_output; + double vblank_gap; + unsigned int last_tv_sec; + unsigned int last_tv_usec; } tdm_server_client_info; typedef struct _tdm_server_vblank_info { struct list_head link; tdm_server_client_info *client_info; struct wl_resource *resource; + + unsigned int req_sec; + unsigned int req_usec; + + tdm_event_loop_source *timer_source; + unsigned int timer_target_sec; + unsigned int timer_target_usec; } tdm_server_vblank_info; static tdm_private_server *keep_private_server; static int tdm_debug_server; static void -_tdm_server_cb_output_vblank(tdm_output *output, unsigned int sequence, - unsigned int tv_sec, unsigned int tv_usec, - void *user_data) +_tdm_server_send_done(tdm_server_vblank_info *vblank_info, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec); + +static tdm_error +_tdm_server_cb_timer(void *user_data) { tdm_server_vblank_info *vblank_info = (tdm_server_vblank_info*)user_data; + + _tdm_server_send_done(vblank_info, 0, + vblank_info->timer_target_sec, + vblank_info->timer_target_usec); + + return TDM_ERROR_NONE; +} + +static tdm_error +_tdm_server_update_timer(tdm_server_vblank_info *vblank_info, int interval) +{ + tdm_server_client_info *client_info = vblank_info->client_info; + tdm_private_server *private_server = client_info->private_server; + tdm_private_loop *private_loop = private_server->private_loop; + unsigned long last, prev_req, req, curr, next; + int ms_delay; + tdm_error ret; + + ret = tdm_display_lock(private_loop->dpy); + TDM_RETURN_VAL_IF_FAIL(ret == TDM_ERROR_NONE, ret); + + if (!vblank_info->timer_source) { + vblank_info->timer_source = + tdm_event_loop_add_timer_handler(private_loop->dpy, + _tdm_server_cb_timer, + vblank_info, + NULL); + if (!vblank_info->timer_source) { + TDM_ERR("couldn't add timer"); + tdm_display_unlock(private_loop->dpy); + return TDM_ERROR_OPERATION_FAILED; + } + } + + last = (unsigned long)client_info->last_tv_sec * 1000000 + client_info->last_tv_usec; + req = (unsigned long)vblank_info->req_sec * 1000000 + vblank_info->req_usec; + curr = tdm_helper_get_time_in_micros(); + + prev_req = last + (unsigned int)((req - last) / client_info->vblank_gap) * client_info->vblank_gap; + next = prev_req + (unsigned long)(client_info->vblank_gap * interval); + + while (next < curr) + next += (unsigned long)client_info->vblank_gap; + + TDM_DBG("last(%.6lu) req(%.6lu) curr(%.6lu) prev_req(%.6lu) next(%.6lu)", + last, req, curr, prev_req, next); + + ms_delay = (int)ceil((double)(next - curr) / 1000); + if (ms_delay == 0) + ms_delay = 1; + + TDM_DBG("delay(%lu) ms_delay(%d)", next - curr, ms_delay); + + ret = tdm_event_loop_source_timer_update(vblank_info->timer_source, ms_delay); + if (ret != TDM_ERROR_NONE) { + tdm_event_loop_source_remove(vblank_info->timer_source); + vblank_info->timer_source = NULL; + tdm_display_unlock(private_loop->dpy); + TDM_ERR("couldn't update timer"); + return TDM_ERROR_OPERATION_FAILED; + } + + TDM_DBG("timer tick: %d us", (int)(next - last)); + + vblank_info->timer_target_sec = next / 1000000; + vblank_info->timer_target_usec = next % 1000000; + + tdm_display_unlock(private_loop->dpy); + + return TDM_ERROR_NONE; +} + +static void +_tdm_server_send_done(tdm_server_vblank_info *vblank_info, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec) +{ tdm_server_vblank_info *found; + tdm_server_client_info *client_info; + unsigned long vtime = (tv_sec * 1000000) + tv_usec; + unsigned long curr = tdm_helper_get_time_in_micros(); if (!keep_private_server) return; @@ -86,23 +182,45 @@ _tdm_server_cb_output_vblank(tdm_output *output, unsigned int sequence, return; } + client_info = vblank_info->client_info; + client_info->last_tv_sec = tv_sec; + client_info->last_tv_usec = tv_usec; + + TDM_DBG("wl_tdm_vblank@%d done. tv(%lu) curr(%lu)", + wl_resource_get_id(vblank_info->resource), vtime, curr); + if (tdm_debug_server) { - unsigned long curr = tdm_helper_get_time_in_micros(); - unsigned long vtime = (tv_sec * 1000000) + tv_usec; if (curr - vtime > 1000) /* 1ms */ TDM_WRN("delay: %d us", (int)(curr - vtime)); } - TDM_DBG("wl_tdm_vblank@%d done", wl_resource_get_id(vblank_info->resource)); - wl_tdm_vblank_send_done(vblank_info->resource, sequence, tv_sec, tv_usec); wl_resource_destroy(vblank_info->resource); } static void +_tdm_server_cb_output_vblank(tdm_output *output, unsigned int sequence, + unsigned int tv_sec, unsigned int tv_usec, + void *user_data) +{ + tdm_server_vblank_info *vblank_info = (tdm_server_vblank_info*)user_data; + + _tdm_server_send_done(vblank_info, sequence, tv_sec, tv_usec); +} + +static void destroy_vblank_callback(struct wl_resource *resource) { tdm_server_vblank_info *vblank_info = wl_resource_get_user_data(resource); + + if (vblank_info->timer_source) { + tdm_private_server *private_server = vblank_info->client_info->private_server; + + tdm_display_lock(private_server->private_loop->dpy); + tdm_event_loop_source_remove(vblank_info->timer_source); + tdm_display_unlock(private_server->private_loop->dpy); + } + LIST_DEL(&vblank_info->link); free(vblank_info); } @@ -127,8 +245,8 @@ _tdm_server_client_cb_wait_vblank(struct wl_client *client, struct wl_resource *vblank_resource; tdm_output *found = NULL; tdm_output_dpms dpms_value = TDM_OUTPUT_DPMS_ON; - int count = 0, i; tdm_error ret; + const char *model; TDM_DBG("The tdm client requests vblank"); @@ -139,26 +257,37 @@ _tdm_server_client_cb_wait_vblank(struct wl_client *client, TDM_WRN("delay(req): %d us", (int)(curr - reqtime)); } - tdm_display_get_output_count(private_loop->dpy, &count); + if (client_info->vblank_output) { + model = NULL; + ret = tdm_output_get_model_info(client_info->vblank_output, NULL, &model, NULL); + if (model && !strncmp(model, name, TDM_NAME_LEN)) + found = client_info->vblank_output; + } - for (i = 0; i < count; i++) { - tdm_output *output= tdm_display_get_output(private_loop->dpy, i, NULL); - tdm_output_conn_status status; - const char *model = NULL; + if (!found) { + int count = 0, i; + + tdm_display_get_output_count(private_loop->dpy, &count); + + for (i = 0; i < count; i++) { + tdm_output *output= tdm_display_get_output(private_loop->dpy, i, NULL); + tdm_output_conn_status status; - ret = tdm_output_get_conn_status(output, &status); - if (ret || status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) - continue; + ret = tdm_output_get_conn_status(output, &status); + if (ret || status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) + continue; - ret = tdm_output_get_model_info(output, NULL, &model, NULL); - if (ret || !model) - continue; + model = NULL; + ret = tdm_output_get_model_info(output, NULL, &model, NULL); + if (ret || !model) + continue; - if (strncmp(model, name, TDM_NAME_LEN)) - continue; + if (strncmp(model, name, TDM_NAME_LEN)) + continue; - found = output; - break; + found = output; + break; + } } if (!found) { @@ -168,6 +297,23 @@ _tdm_server_client_cb_wait_vblank(struct wl_client *client, return; } + if (client_info->vblank_output != found) { + const tdm_output_mode *mode = NULL; + + client_info->vblank_output = found; + + tdm_output_get_mode(client_info->vblank_output, &mode); + if (!mode || mode->vrefresh <= 0) { + wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED, + "couldn't get mode of %s", name); + TDM_ERR("couldn't get mode of %s", name); + return; + } + + client_info->vblank_gap = (double)1000000 / mode->vrefresh; + TDM_INFO("vblank_gap(%.6lf)", client_info->vblank_gap); + } + tdm_output_get_dpms(found, &dpms_value); if (dpms_value != TDM_OUTPUT_DPMS_ON && !sw_timer) { @@ -195,6 +341,11 @@ _tdm_server_client_cb_wait_vblank(struct wl_client *client, goto free_info; } + vblank_info->resource = vblank_resource; + vblank_info->client_info = client_info; + vblank_info->req_sec = req_sec; + vblank_info->req_usec = req_usec; + if (dpms_value == TDM_OUTPUT_DPMS_ON) { ret = tdm_output_wait_vblank(found, interval, 0, _tdm_server_cb_output_vblank, vblank_info); @@ -205,11 +356,11 @@ _tdm_server_client_cb_wait_vblank(struct wl_client *client, goto destroy_resource; } } else if (sw_timer) { - /* TODO: need to implement things related with sw_timer */ + ret = _tdm_server_update_timer(vblank_info, interval); if (ret != TDM_ERROR_NONE) { wl_resource_post_error(resource, WL_TDM_CLIENT_ERROR_OPERATION_FAILED, - "not implemented yet"); - TDM_ERR("not implemented yet"); + "couldn't update timer for %s", name); + TDM_ERR("couldn't update timer for %s", name); goto destroy_resource; } } else { @@ -219,9 +370,6 @@ _tdm_server_client_cb_wait_vblank(struct wl_client *client, goto destroy_resource; } - vblank_info->resource = vblank_resource; - vblank_info->client_info = client_info; - wl_resource_set_implementation(vblank_resource, NULL, vblank_info, destroy_vblank_callback); @@ -242,7 +390,6 @@ static void destroy_client_callback(struct wl_resource *resource) { tdm_server_client_info *client_info = wl_resource_get_user_data(resource); - LIST_DEL(&client_info->link); free(client_info); } -- 2.7.4 From c72e8e26694806272ee52de4ed2664ee74ac1b2a Mon Sep 17 00:00:00 2001 From: Changyeon Lee Date: Tue, 3 May 2016 20:32:58 +0900 Subject: [PATCH 04/16] Fix null check after deref Change-Id: I0ebe88f8bde7d6feaaa538f2b8fb78f36406b4e2 --- src/tdm_capture.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tdm_capture.c b/src/tdm_capture.c index 85432d3..3d36ed4 100644 --- a/src/tdm_capture.c +++ b/src/tdm_capture.c @@ -265,7 +265,7 @@ tdm_capture_create_layer_internal(tdm_private_layer *private_layer, INTERN void tdm_capture_destroy_internal(tdm_private_capture *private_capture) { - tdm_private_display *private_display = private_capture->private_display; + tdm_private_display *private_display; tdm_func_capture *func_capture; tdm_buffer_info *b = NULL, *bb = NULL; @@ -274,6 +274,8 @@ tdm_capture_destroy_internal(tdm_private_capture *private_capture) if (!private_capture) return; + private_display = private_capture->private_display; + LIST_DEL(&private_capture->link); LIST_DEL(&private_capture->display_link); -- 2.7.4 From 9dbc679444ef59a2e9f1185965d1f9d67f20b568 Mon Sep 17 00:00:00 2001 From: Changyeon Lee Date: Tue, 3 May 2016 20:47:47 +0900 Subject: [PATCH 05/16] Fix double free Change-Id: I0a1987af5eb0679187fb2bc398ca93bed95453e5 --- src/tdm.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/tdm.c b/src/tdm.c index acc9c05..ada9f29 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -90,30 +90,42 @@ tdm_display_find_output_stamp(tdm_private_display *private_display, static void _tdm_display_destroy_caps_pp(tdm_caps_pp *caps_pp) { - free(caps_pp->formats); + if (caps_pp->formats) + free(caps_pp->formats); + memset(caps_pp, 0, sizeof(tdm_caps_pp)); } static void _tdm_display_destroy_caps_capture(tdm_caps_capture *caps_capture) { - free(caps_capture->formats); + if (caps_capture->formats) + free(caps_capture->formats); + memset(caps_capture, 0, sizeof(tdm_caps_capture)); } static void _tdm_display_destroy_caps_layer(tdm_caps_layer *caps_layer) { - free(caps_layer->formats); - free(caps_layer->props); + if (caps_layer->formats) + free(caps_layer->formats); + + if (caps_layer->props) + free(caps_layer->props); + memset(caps_layer, 0, sizeof(tdm_caps_layer)); } static void _tdm_display_destroy_caps_output(tdm_caps_output *caps_output) { - free(caps_output->modes); - free(caps_output->props); + if (caps_output->modes) + free(caps_output->modes); + + if (caps_output->props) + free(caps_output->props); + memset(caps_output, 0, sizeof(tdm_caps_output)); } -- 2.7.4 From e41991b36d60e5a79b7703c3cb1f2753dc6478ea Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 10 May 2016 11:58:39 +0900 Subject: [PATCH 06/16] doxygen update Change-Id: I285ea555ae3dc43fd007f926001b9d205c12b751 --- doc/tdm_doc.h | 31 ++++++++++++++-------------- include/tdm_backend.h | 56 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 66 insertions(+), 21 deletions(-) diff --git a/doc/tdm_doc.h b/doc/tdm_doc.h index 7d4c856..54711d7 100644 --- a/doc/tdm_doc.h +++ b/doc/tdm_doc.h @@ -76,35 +76,36 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @code #include - static tdm_func_display drm_func_display = { - drm_display_get_capabilitiy, - ... - }; - - static tdm_func_output drm_func_output = { - drm_output_get_capability, - ... - }; - - static tdm_func_layer drm_func_layer = { - drm_layer_get_capability, - ... - }; - static tdm_drm_data *drm_data; tdm_backend_data* tdm_drm_init(tdm_display *dpy, tdm_error *error) { + tdm_func_display drm_func_display; + tdm_func_output drm_func_output; + tdm_func_layer drm_func_layer; + ... drm_data = calloc(1, sizeof(tdm_drm_data)); ... + + memset(&drm_func_display, 0, sizeof(drm_func_display)); + drm_func_display.display_get_capabilitiy = drm_display_get_capabilitiy; + ... ret = tdm_backend_register_func_display(dpy, &drm_func_display); if (ret != TDM_ERROR_NONE) goto failed; + + memset(&drm_func_output, 0, sizeof(drm_func_output)); + drm_func_output.output_get_capability = drm_output_get_capability; + ... ret = tdm_backend_register_func_output(dpy, &drm_func_output); if (ret != TDM_ERROR_NONE) goto failed; + + memset(&drm_func_layer, 0, sizeof(drm_func_layer)); + drm_func_layer.layer_get_capability = drm_layer_get_capability; + ... ret = tdm_backend_register_func_layer(dpy, &drm_func_layer); if (ret != TDM_ERROR_NONE) goto failed; diff --git a/include/tdm_backend.h b/include/tdm_backend.h index ca4c420..e74a7ed 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -388,7 +388,9 @@ typedef struct _tdm_func_output { * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @see output_set_vblank_handler, tdm_output_vblank_handler * @remark - * A backend module @b SHOULD call a user vblank handler after interval vblanks. + * If this function returns TDM_ERROR_NONE, a backend module @b SHOULD call + * a user vblank handler with the user data of this function after interval + * vblanks. */ tdm_error (*output_wait_vblank)(tdm_output *output, int interval, int sync, void *user_data); @@ -410,8 +412,12 @@ typedef struct _tdm_func_output { * @return #TDM_ERROR_NONE if success. Otherwise, error value. * @see output_set_commit_handler, tdm_output_commit_handler * @remark - * A backend module @b SHOULD call a user commit handler after all change of - * a output object are applied. + * When this function is called, a backend module @b SHOULD apply the all + * changes of the given output object to screen as well as the layer changes + * of this output. + * If this function returns TDM_ERROR_NONE, a backend module @b SHOULD call + * a user commit handler with the user data of this function after all + * changes of the given output object are applied. */ tdm_error (*output_commit)(tdm_output *output, int sync, void *user_data); @@ -470,12 +476,14 @@ typedef struct _tdm_func_output { /** * @brief Set a output connection status handler - * @details The handler will be called when the connection status of a - * output object is changed. + * @details A backend module need to call the output status handler when the + * output connection status has been changed to let the TDM frontend know + * the change. * @param[in] output A output object * @param[in] func A output status handler * @param[in] user_data The user data * @return #TDM_ERROR_NONE if success. Otherwise, error value. + * @since 1.1.0 */ tdm_error (*output_set_status_handler)(tdm_output *output, tdm_output_status_handler func, @@ -938,22 +946,58 @@ void tdm_buffer_remove_destroy_handler(tbm_surface_h buffer, tdm_buffer_destroy_handler func, void *user_data); - +/** + * @brief Add a FD handler for activity on the given file descriptor + * @param[in] dpy A display object + * @param[in] fd A file descriptor + * @param[in] mask to monitor FD + * @param[in] func A FD handler function + * @param[in] user_data user data + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A FD event source + * @see #tdm_event_loop_source_fd_update, #tdm_event_loop_source_remove + */ tdm_event_loop_source* tdm_event_loop_add_fd_handler(tdm_display *dpy, int fd, tdm_event_loop_mask mask, tdm_event_loop_fd_handler func, void *user_data, tdm_error *error); +/** + * @brief Update the mask of the given FD event source + * @param[in] source The given FD event source + * @param[in] mask to monitor FD + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ tdm_error tdm_event_loop_source_fd_update(tdm_event_loop_source *source, tdm_event_loop_mask mask); +/** + * @brief Add a timer handler + * @param[in] dpy A display object + * @param[in] func A timer handler function + * @param[in] user_data user data + * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value. + * @return A timer event source + * @see #tdm_event_loop_source_timer_update, #tdm_event_loop_source_remove + */ tdm_event_loop_source* tdm_event_loop_add_timer_handler(tdm_display *dpy, tdm_event_loop_timer_handler func, void *user_data, tdm_error *error); +/** + * @brief Update the millisecond delay time of the given timer event source. + * @param[in] source The given timer event source + * @param[in] ms_delay The millisecond delay time. zero "0" disarms the timer. + * @return #TDM_ERROR_NONE if success. Otherwise, error value. + */ tdm_error tdm_event_loop_source_timer_update(tdm_event_loop_source *source, int ms_delay); +/** + * @brief Remove the given event source + * @param[in] source The given event source + * @see #tdm_event_loop_add_fd_handler, #tdm_event_loop_add_timer_handler + */ void tdm_event_loop_source_remove(tdm_event_loop_source *source); -- 2.7.4 From ef9d0ba30e9681f4390ebed6c22314de1a9db91f Mon Sep 17 00:00:00 2001 From: Boram Park Date: Tue, 10 May 2016 11:59:50 +0900 Subject: [PATCH 07/16] change input param's type Change-Id: I3044cf96eabe8f2e5d20af38aa3e8aecee088ff0 --- include/tdm_backend.h | 2 +- src/tdm_event_loop.c | 2 +- src/tdm_server.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/tdm_backend.h b/include/tdm_backend.h index e74a7ed..e114d3c 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -991,7 +991,7 @@ tdm_event_loop_add_timer_handler(tdm_display *dpy, tdm_event_loop_timer_handler * @return #TDM_ERROR_NONE if success. Otherwise, error value. */ tdm_error -tdm_event_loop_source_timer_update(tdm_event_loop_source *source, int ms_delay); +tdm_event_loop_source_timer_update(tdm_event_loop_source *source, unsigned int ms_delay); /** * @brief Remove the given event source diff --git a/src/tdm_event_loop.c b/src/tdm_event_loop.c index c6d3486..d1eb6ea 100644 --- a/src/tdm_event_loop.c +++ b/src/tdm_event_loop.c @@ -430,7 +430,7 @@ tdm_event_loop_add_timer_handler(tdm_display *dpy, tdm_event_loop_timer_handler } EXTERN tdm_error -tdm_event_loop_source_timer_update(tdm_event_loop_source *source, int ms_delay) +tdm_event_loop_source_timer_update(tdm_event_loop_source *source, unsigned int ms_delay) { tdm_event_loop_source_timer *timer_source = source; diff --git a/src/tdm_server.c b/src/tdm_server.c index 41538ef..b053d47 100644 --- a/src/tdm_server.c +++ b/src/tdm_server.c @@ -106,7 +106,7 @@ _tdm_server_update_timer(tdm_server_vblank_info *vblank_info, int interval) tdm_private_server *private_server = client_info->private_server; tdm_private_loop *private_loop = private_server->private_loop; unsigned long last, prev_req, req, curr, next; - int ms_delay; + unsigned int ms_delay; tdm_error ret; ret = tdm_display_lock(private_loop->dpy); @@ -138,7 +138,7 @@ _tdm_server_update_timer(tdm_server_vblank_info *vblank_info, int interval) TDM_DBG("last(%.6lu) req(%.6lu) curr(%.6lu) prev_req(%.6lu) next(%.6lu)", last, req, curr, prev_req, next); - ms_delay = (int)ceil((double)(next - curr) / 1000); + ms_delay = (unsigned int)ceil((double)(next - curr) / 1000); if (ms_delay == 0) ms_delay = 1; -- 2.7.4 From 33aca53f00924cf8055f5ef92ffff3d96eefb633 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 11 May 2016 08:50:53 +0900 Subject: [PATCH 08/16] fix coding style Change-Id: Ieea4beecaa9e22bfbebb742390568d35301e090a --- src/tdm.c | 4 ++-- src/tdm_capture.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tdm.c b/src/tdm.c index ada9f29..fef325e 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -90,7 +90,7 @@ tdm_display_find_output_stamp(tdm_private_display *private_display, static void _tdm_display_destroy_caps_pp(tdm_caps_pp *caps_pp) { - if (caps_pp->formats) + if (caps_pp->formats) free(caps_pp->formats); memset(caps_pp, 0, sizeof(tdm_caps_pp)); @@ -99,7 +99,7 @@ _tdm_display_destroy_caps_pp(tdm_caps_pp *caps_pp) static void _tdm_display_destroy_caps_capture(tdm_caps_capture *caps_capture) { - if (caps_capture->formats) + if (caps_capture->formats) free(caps_capture->formats); memset(caps_capture, 0, sizeof(tdm_caps_capture)); diff --git a/src/tdm_capture.c b/src/tdm_capture.c index 3d36ed4..01f3e8b 100644 --- a/src/tdm_capture.c +++ b/src/tdm_capture.c @@ -274,7 +274,7 @@ tdm_capture_destroy_internal(tdm_private_capture *private_capture) if (!private_capture) return; - private_display = private_capture->private_display; + private_display = private_capture->private_display; LIST_DEL(&private_capture->link); LIST_DEL(&private_capture->display_link); -- 2.7.4 From 34baff16d98c725c6e170c530f0ab72373b17a8f Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 11 May 2016 09:34:53 +0900 Subject: [PATCH 09/16] add LIST_LENGTH macro Change-Id: I5a64f1416aadde167a51a1ed5bcd3adeae2be12d --- include/tdm_list.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/tdm_list.h b/include/tdm_list.h index acdf6c4..a6d6bbb 100644 --- a/include/tdm_list.h +++ b/include/tdm_list.h @@ -87,12 +87,27 @@ static inline void list_delinit(struct list_head *item) item->prev = item; } +static inline int list_length(struct list_head *item) +{ + struct list_head *next; + int length = 0; + + next = item->next; + while (next != item) { + length++; + next = next->next; + } + + return length; +} + #define LIST_INITHEAD(__item) list_inithead(__item) #define LIST_ADD(__item, __list) list_add(__item, __list) #define LIST_ADDTAIL(__item, __list) list_addtail(__item, __list) #define LIST_REPLACE(__from, __to) list_replace(__from, __to) #define LIST_DEL(__item) list_del(__item) #define LIST_DELINIT(__item) list_delinit(__item) +#define LIST_LENGTH(__item) list_length(__item) #define LIST_ENTRY(__type, __item, __field) \ ((__type *)(((char *)(__item)) - offsetof(__type, __field))) -- 2.7.4 From b67980781d02f7bf478d14123f11a186207da9d0 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 11 May 2016 09:15:38 +0900 Subject: [PATCH 10/16] add tdm_display_check_module_abi function Change-Id: I6d60a6895978f57d94a8238755c8377a3373749f --- src/tdm.c | 14 ++++++++++++++ src/tdm_private.h | 3 +++ 2 files changed, 17 insertions(+) diff --git a/src/tdm.c b/src/tdm.c index fef325e..46d5b61 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -1031,3 +1031,17 @@ tdm_display_deinit(tdm_display *dpy) TDM_INFO("done"); } +INTERN int +tdm_display_check_module_abi(tdm_private_display *private_display, int abimaj, int abimin) +{ + tdm_backend_module *module = private_display->module_data; + + if (TDM_BACKEND_GET_ABI_MAJOR(module->abi_version) < abimaj) + return 0; + + if (TDM_BACKEND_GET_ABI_MINOR(module->abi_version) < abimin) + return 0; + + return 1; +} + diff --git a/src/tdm_private.h b/src/tdm_private.h index 671df8b..134ce51 100644 --- a/src/tdm_private.h +++ b/src/tdm_private.h @@ -306,6 +306,9 @@ typedef struct _tdm_buffer_info { const char* tdm_get_dpms_str(tdm_output_dpms dpms_value); +int +tdm_display_check_module_abi(tdm_private_display *private_display, int abimaj, int abimin); + tdm_private_output * tdm_display_find_output_stamp(tdm_private_display *private_display, unsigned long stamp); -- 2.7.4 From 5279a9ec05fb661f20959a02199712c60fb46bfa Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 11 May 2016 08:41:28 +0900 Subject: [PATCH 11/16] add max_attach_count variable Change-Id: Ib5f6801a489232a1ed45c83809fefa527a601a51 --- include/tdm_backend.h | 10 ++++++++++ src/tdm_pp.c | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/include/tdm_backend.h b/include/tdm_backend.h index e114d3c..596ca78 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -156,6 +156,11 @@ typedef struct _tdm_caps_pp { int max_w; /**< The maximum width. -1 means "not defined" */ int max_h; /**< The maximum height. -1 means "not defined" */ int preferred_align; /**< The prefered align. -1 means "not defined" */ + + int max_attach_count; /**< The attach count which a PP object can handle. + * -1 means "not defined". + * @since 1.2.0 + */ } tdm_caps_pp; /** @@ -174,6 +179,11 @@ typedef struct _tdm_caps_capture { int max_w; /**< The maximum width. -1 means "not defined" */ int max_h; /**< The maximum height. -1 means "not defined" */ int preferred_align; /**< The prefered align. -1 means "not defined" */ + + int max_attach_count; /**< The attach count which a capture object can handle. + * -1 means "not defined". + * @since 1.2.0 + */ } tdm_caps_capture; /** diff --git a/src/tdm_pp.c b/src/tdm_pp.c index 0b56073..c574d8a 100644 --- a/src/tdm_pp.c +++ b/src/tdm_pp.c @@ -360,6 +360,18 @@ tdm_pp_attach(tdm_pp *pp, tbm_surface_h src, tbm_surface_h dst) return TDM_ERROR_NOT_IMPLEMENTED; } + if (tdm_display_check_module_abi(private_display, 1, 2) && + private_display->caps_pp.max_attach_count > 0) { + int length = LIST_LENGTH(&private_pp->src_pending_buffer_list) + + LIST_LENGTH(&private_pp->src_buffer_list); + if (length >= private_display->caps_pp.max_attach_count) { + _pthread_mutex_unlock(&private_display->lock); + TDM_DBG("failed: too many attached!! max_attach_count(%d)", + private_display->caps_pp.max_attach_count); + return TDM_ERROR_BAD_REQUEST; + } + } + ret = _tdm_pp_check_if_exist(private_pp, src, dst); if (ret != TDM_ERROR_NONE) { _pthread_mutex_unlock(&private_display->lock); -- 2.7.4 From 1be6524fa52b771c23acbadb8de79a8c51557eba Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 11 May 2016 10:28:20 +0900 Subject: [PATCH 12/16] get version in configuration time Change-Id: Ib3e2fbe010bc1f80109257b4fcee81c99032d7aa --- configure.ac | 19 ++++++++++++++++++- doc/{tdm_doc.h => tdm_doc.h.in} | 3 +-- libtdm.pc.in | 2 +- src/tdm.c | 17 ++++++++--------- 4 files changed, 28 insertions(+), 13 deletions(-) rename doc/{tdm_doc.h => tdm_doc.h.in} (99%) diff --git a/configure.ac b/configure.ac index 84bdb79..8ead459 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,13 @@ AC_PREREQ([2.60]) + +m4_define([tdm_major_version], [1]) +m4_define([tdm_minor_version], [1]) +m4_define([tdm_micro_version], [0]) +m4_define([tdm_version], + [tdm_major_version.tdm_minor_version.tdm_micro_version]) + AC_INIT([libtdm], - [1.1.0], + [tdm_version], [https://www.tizen.org], [libtdm]) @@ -55,11 +62,21 @@ AC_ARG_WITH(tdm-module-path, AS_HELP_STRING([--with-tdm-module-path=PATH], [tdm [ TDM_MODULE_PATH="${DEFAULT_TDM_MODULE_PATH}" ]) AC_DEFINE_UNQUOTED(TDM_MODULE_PATH, "${TDM_MODULE_PATH}", [Directory for the modules of tdm]) +AC_SUBST([TDM_MAJOR_VERSION], [tdm_major_version]) +AC_SUBST([TDM_MINOR_VERSION], [tdm_minor_version]) +AC_SUBST([TDM_MICRO_VERSION], [tdm_micro_version]) +AC_SUBST([TDM_VERSION], [tdm_version]) + +AC_DEFINE([TDM_MAJOR_VERSION], [tdm_major_version], [tdm ABI major version]) +AC_DEFINE([TDM_MINOR_VERSION], [tdm_minor_version], [tdm ABI minor version]) +AC_DEFINE([TDM_MICRO_VERSION], [tdm_micro_version], [tdm ABI macro version]) + # For enumerating devices in test case AC_SUBST(WARN_CFLAGS) AC_OUTPUT([ Makefile libtdm.pc + doc/tdm_doc.h include/Makefile protocol/Makefile client/libtdm-client.pc diff --git a/doc/tdm_doc.h b/doc/tdm_doc.h.in similarity index 99% rename from doc/tdm_doc.h rename to doc/tdm_doc.h.in index 54711d7..3d5b9e6 100644 --- a/doc/tdm_doc.h +++ b/doc/tdm_doc.h.in @@ -39,8 +39,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * @mainpage TDM * @author Boram Park, boram1288.park@samsung.com - * @date Mar 17, 2016 - * @version 1.1.0 + * @version @TDM_VERSION@ * @par Introduction * TDM stands for Tizen Display Manager. It's the display HAL layer for tizen * display server. It offers the frontend APIs(@ref tdm.h) for a frontend user diff --git a/libtdm.pc.in b/libtdm.pc.in index 536e59b..8c2adf4 100644 --- a/libtdm.pc.in +++ b/libtdm.pc.in @@ -5,7 +5,7 @@ includedir=@includedir@ Name: libtdm Description: Tizen Display Manager Library -Version: @PACKAGE_VERSION@ +Version: @TDM_VERSION@ Requires: libtbm Libs: -L${libdir} -ltdm Cflags: -I${includedir} diff --git a/src/tdm.c b/src/tdm.c index 46d5b61..f073d21 100644 --- a/src/tdm.c +++ b/src/tdm.c @@ -675,12 +675,9 @@ _tdm_display_check_module(tdm_backend_module *module) const char *name; const char *vendor; int major, minor; - int abimaj, abimin; - abimaj = TDM_BACKEND_GET_ABI_MAJOR(TDM_BACKEND_ABI_VERSION); - abimin = TDM_BACKEND_GET_ABI_MINOR(TDM_BACKEND_ABI_VERSION); - - TDM_INFO("TDM module ABI version : %d.%d", abimaj, abimin); + TDM_INFO("TDM ABI version : %d.%d", + TDM_MAJOR_VERSION, TDM_MINOR_VERSION); name = module->name ? module->name : "unknown"; vendor = module->vendor ? module->vendor : "unknown"; @@ -691,13 +688,15 @@ _tdm_display_check_module(tdm_backend_module *module) TDM_INFO("'%s' vendor: %s", name, vendor); TDM_INFO("'%s' version: %d.%d", name, major, minor); - if (major != abimaj) { - TDM_ERR("'%s' major version mismatch, %d != %d", name, major, abimaj); + if (major != TDM_MAJOR_VERSION) { + TDM_ERR("'%s' major version mismatch, %d != %d", + name, major, TDM_MAJOR_VERSION); return TDM_ERROR_BAD_MODULE; } - if (minor > abimin) { - TDM_ERR("'%s' minor version(%d) is newer than %d", name, minor, abimin); + if (minor > TDM_MINOR_VERSION) { + TDM_ERR("'%s' minor version(%d) is newer than %d", + name, minor, TDM_MINOR_VERSION); return TDM_ERROR_BAD_MODULE; } -- 2.7.4 From 0ad75687eb05c25aadbde60c3ac38e5d8945e3db Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 11 May 2016 10:45:09 +0900 Subject: [PATCH 13/16] deprecate TDM_BACKEND_ABI_VERSION Use TDM_BACKEND_SET_ABI_VERSION instead of this. Change-Id: Ice6b35a22eac2bf23ef18854abe41033091bac4f --- include/tdm_backend.h | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/include/tdm_backend.h b/include/tdm_backend.h index 596ca78..4bde8b0 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -782,23 +782,39 @@ typedef struct _tdm_func_capture { void (*reserved8)(void); } tdm_func_capture; -/* - * ABI versions. Each version has a major and minor revision. Modules - * using lower minor revisions must work with servers of a higher minor - * revision. There is no compatibility between different major revisions. - * Whenever the ABI_ANSIC_VERSION is changed, the others must also be - * changed. The minor revision mask is 0x0000FFFF and the major revision - * mask is 0xFFFF0000. - */ #define TDM_BACKEND_MINOR_VERSION_MASK 0x0000FFFF #define TDM_BACKEND_MAJOR_VERSION_MASK 0xFFFF0000 #define TDM_BACKEND_GET_ABI_MINOR(v) ((v) & TDM_BACKEND_MINOR_VERSION_MASK) #define TDM_BACKEND_GET_ABI_MAJOR(v) (((v) & TDM_BACKEND_MAJOR_VERSION_MASK) >> 16) -#define SET_TDM_BACKEND_ABI_VERSION(major, minor) \ +/** + * @brief + * The ABI version of TDM backend module. It has a major and minor revision. + * Modules using lower minor revisions will work with TDM frontend of a higher + * minor revision. There is no compatibility between different major revisions. + * The minor revision mask is 0x0000FFFF and the major revision mask is 0xFFFF0000. + * @par Example + * @code + tdm_backend_module tdm_backend_module_data = { + "drm", + "Samsung", + TDM_BACKEND_SET_ABI_VERSION(1,1), + tdm_drm_init, + tdm_drm_deinit + }; + * @endcode + */ +#define TDM_BACKEND_SET_ABI_VERSION(major, minor) \ (((major) << 16) & TDM_BACKEND_MAJOR_VERSION_MASK) | \ ((major) & TDM_BACKEND_MINOR_VERSION_MASK) -#define TDM_BACKEND_ABI_VERSION SET_TDM_BACKEND_ABI_VERSION(1, 1) + +/** + * @brief + * This MACRO is deprecated since 1.2.0. Use TDM_BACKEND_SET_ABI_VERSION instead of this. + * @deprecated + * Use #TDM_BACKEND_SET_ABI_VERSION macro instead of this macro. + */ +#define TDM_BACKEND_ABI_VERSION TDM_BACKEND_SET_ABI_VERSION(1, 1) /** * @brief The backend module information of the entry point to initialize a TDM -- 2.7.4 From fc0354984b353a7bc3a504294621be05d6d8522a Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 11 May 2016 11:02:27 +0900 Subject: [PATCH 14/16] version up to 1.2.0 Change-Id: I23b875888896f4b3d197f37a425e50823bc0c4d3 --- configure.ac | 2 +- packaging/libtdm.spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 8ead459..4f01243 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.60]) m4_define([tdm_major_version], [1]) -m4_define([tdm_minor_version], [1]) +m4_define([tdm_minor_version], [2]) m4_define([tdm_micro_version], [0]) m4_define([tdm_version], [tdm_major_version.tdm_minor_version.tdm_micro_version]) diff --git a/packaging/libtdm.spec b/packaging/libtdm.spec index 6df4f07..881db02 100644 --- a/packaging/libtdm.spec +++ b/packaging/libtdm.spec @@ -1,5 +1,5 @@ Name: libtdm -Version: 1.1.0 +Version: 1.2.0 Release: 0 Summary: User Library of Tizen Display Manager Group: Development/Libraries -- 2.7.4 From 7449e59bf1928ba857a189e8e70584f9f6fec744 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 11 May 2016 14:30:11 +0900 Subject: [PATCH 15/16] use the fixed version for doxygen Change-Id: I213ffd20f69bace853074a829536564c3448515e --- configure.ac | 1 - doc/{tdm_doc.h.in => tdm_doc.h} | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) rename doc/{tdm_doc.h.in => tdm_doc.h} (98%) diff --git a/configure.ac b/configure.ac index 4f01243..fa99a81 100644 --- a/configure.ac +++ b/configure.ac @@ -76,7 +76,6 @@ AC_SUBST(WARN_CFLAGS) AC_OUTPUT([ Makefile libtdm.pc - doc/tdm_doc.h include/Makefile protocol/Makefile client/libtdm-client.pc diff --git a/doc/tdm_doc.h.in b/doc/tdm_doc.h similarity index 98% rename from doc/tdm_doc.h.in rename to doc/tdm_doc.h index 3d5b9e6..4d60a64 100644 --- a/doc/tdm_doc.h.in +++ b/doc/tdm_doc.h @@ -39,7 +39,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * @mainpage TDM * @author Boram Park, boram1288.park@samsung.com - * @version @TDM_VERSION@ + * @version 1.2.0 * @par Introduction * TDM stands for Tizen Display Manager. It's the display HAL layer for tizen * display server. It offers the frontend APIs(@ref tdm.h) for a frontend user @@ -123,7 +123,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. { "drm", "Samsung", - TDM_BACKEND_ABI_VERSION, + TDM_BACKEND_SET_ABI_VERSION(1,2), tdm_drm_init, tdm_drm_deinit }; -- 2.7.4 From 40ad17404d1c0398a7bebdd039fab420a03c9598 Mon Sep 17 00:00:00 2001 From: Boram Park Date: Wed, 11 May 2016 14:39:19 +0900 Subject: [PATCH 16/16] doxygen update Change-Id: I75908f664daa551d39b5a33d1c938aaefba03a4a --- include/tdm_backend.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/tdm_backend.h b/include/tdm_backend.h index 4bde8b0..9a1ea41 100644 --- a/include/tdm_backend.h +++ b/include/tdm_backend.h @@ -737,8 +737,8 @@ typedef struct _tdm_func_capture { /** * @brief Attach a TDM buffer to a capture object - * @details When capture_commit() function is called, a backend module dumps - * a output or a layer to a TDM buffer. + * @details When capture_commit() function is called, a backend module starts + * to dump a output or a layer to a TDM buffer. * @param[in] capture A capture object * @param[in] buffer A TDM buffer * @return #TDM_ERROR_NONE if success. Otherwise, error value. @@ -746,7 +746,7 @@ typedef struct _tdm_func_capture { * @see capture_commit() function of #tdm_func_capture * @see capture_set_done_handler, tdm_capture_done_handler * @remark - * A backend module dumps a output or a layer to to a TDM buffer when + * A backend module starts to dump a output or a layer to to a TDM buffer when * committed. The size/crop/transform information is set via #capture_set_info() * of #tdm_func_capture. When done, a backend module @b SHOULD return the TDM * buffer via tdm_capture_done_handler. -- 2.7.4