From: Dongwoo Lee Date: Thu, 20 Oct 2022 07:27:30 +0000 (+0900) Subject: resource-manager: add system resource manager library X-Git-Tag: accepted/tizen/unified/20221206.170548~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=fde567b36a3cfcc53b3eecfc79bcdadb318d5eb0;p=platform%2Fcore%2Fsystem%2Flibsyscommon.git resource-manager: add system resource manager library To provide system-wide unified resource framework, resource utilities in resource monitor is extracted into this library. Change-Id: I7ba90e3b1fda1b525a015bb46addd4d860045d6d Signed-off-by: Dongwoo Lee Signed-off-by: Youngjae Cho --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 2df65ef..482712d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,8 @@ SET(libsyscommon_SRCS src/libcommon/ini-parser.c src/libcommon/file.c src/libcommon/common.c + src/resource-manager/resource-manager.c + src/resource-manager/resource-device.c ) SET(HEADERS src/libgdbus/libgdbus.h @@ -26,6 +28,9 @@ SET(HEADERS src/libcommon/ini-parser.h src/libcommon/file.h src/libcommon/common.h + src/resource-manager/resource-manager.h + src/resource-manager/resource-type.h + src/resource-manager/resource-device.h ) # CHECK PKG @@ -35,6 +40,7 @@ pkg_check_modules(syscommon REQUIRED gio-2.0 gio-unix-2.0 dlog + json-c capi-system-info) FOREACH(flag ${syscommon_CFLAGS}) diff --git a/packaging/libsyscommon.spec b/packaging/libsyscommon.spec index d18dac8..c46dab1 100644 --- a/packaging/libsyscommon.spec +++ b/packaging/libsyscommon.spec @@ -13,6 +13,7 @@ BuildRequires: pkgconfig(glib-2.0) >= 2.44 BuildRequires: pkgconfig(gio-2.0) >= 2.44 BuildRequires: pkgconfig(gio-unix-2.0) BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(json-c) BuildRequires: pkgconfig(capi-system-info) Requires: /bin/cp @@ -29,6 +30,7 @@ Requires: libsyscommon = %{version} Requires: pkgconfig(gio-2.0) Requires: pkgconfig(gio-unix-2.0) Requires: pkgconfig(dlog) +Requires: pkgconfig(json-c) Requires: pkgconfig(capi-system-info) %description -n libsyscommon-devel diff --git a/src/resource-manager/resource-device.c b/src/resource-manager/resource-device.c new file mode 100644 index 0000000..fccc969 --- /dev/null +++ b/src/resource-manager/resource-device.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "resource-device.h" + +static GList *g_resource_device_head; + +int syscommon_resman_get_resource_device_count(int resource_type) +{ + GList *node; + struct syscommon_resman_resource_device *device; + int count = 0; + + for (node = g_resource_device_head; node != NULL; node = node->next) { + device = node->data; + if (device->type == resource_type) + count++; + } + + return count; +} + +const struct syscommon_resman_resource_device * +syscommon_resman_find_resource_device(int resource_type, int resource_index) +{ + GList *node; + const struct syscommon_resman_resource_device *device; + + for (node = g_resource_device_head; node != NULL; node = node->next) { + device = node->data; + if (device->type == resource_type && + device->index == resource_index) + return device; + } + + return NULL; +} + +int syscommon_resman_add_resource_device(struct syscommon_resman_resource_device *device) +{ + int count; + + if (!device) + return -EINVAL; + + count = syscommon_resman_get_resource_device_count(device->type); + if (count < 0) + return count; + + device->index = count; + + g_resource_device_head = + g_list_append(g_resource_device_head, (gpointer)device); + + return 0; +} + +void syscommon_resman_remove_resource_device(struct syscommon_resman_resource_device *device) +{ + if (!device) + return; + + g_resource_device_head = + g_list_remove(g_resource_device_head, (gpointer)device); +} diff --git a/src/resource-manager/resource-device.h b/src/resource-manager/resource-device.h new file mode 100644 index 0000000..fd8a99e --- /dev/null +++ b/src/resource-manager/resource-device.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __RESOURCE_DEVICE_H__ +#define __RESOURCE_DEVICE_H__ + +struct syscommon_resman_resource_device { + char *name; + int type; + + /* + * Never initialize it by user of add_resource_device(). + * It will be initialized by add_resource_device function automatically. + */ + int index; +}; + +int syscommon_resman_get_resource_device_count(int resource_type); +const struct syscommon_resman_resource_device * +syscommon_resman_find_resource_device(int resource_type, int resource_index); +int syscommon_resman_add_resource_device(struct syscommon_resman_resource_device *resource_device); +void syscommon_resman_remove_resource_device(struct syscommon_resman_resource_device *resource_device); + +#endif diff --git a/src/resource-manager/resource-manager.c b/src/resource-manager/resource-manager.c new file mode 100644 index 0000000..7faa87b --- /dev/null +++ b/src/resource-manager/resource-manager.c @@ -0,0 +1,1050 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "resource-manager.h" +#include "resource-type.h" + +#define RESOURCE_ATTR_MASK (ULLONG_MAX) +#define BIT64_INDEX(id) (63 - __builtin_clzll(id)) +#define RESOURCE_ATTR_INDEX(id) BIT64_INDEX(id) +#define RESOURCE_CTRL_INDEX(id) BIT64_INDEX(id) +#define RESOURCE_FLAG_VISIBILITY_MASK (SYSCOMMON_RESMAN_RESOURCE_FLAG_PRIVATE \ + | SYSCOMMON_RESMAN_RESOURCE_FLAG_PUBLIC) +#define RESOURCE_ATTR_FLAG_VISIBILITY_MASK (SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE \ + | SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PUBLIC) + + +static GList *g_resource_driver_head; + +static gint __compare_resource_type(gconstpointer data, gconstpointer input) +{ + struct syscommon_resman_resource_driver *driver; + int type = *(int *)input; + + driver = (struct syscommon_resman_resource_driver *)data; + + if (driver && driver->type == type) + return 0; + return -1; +} + +const struct syscommon_resman_resource_driver * +syscommon_resman_find_resource_driver(int resource_type) +{ + GList *node; + + node = g_list_find_custom(g_resource_driver_head, + &resource_type, __compare_resource_type); + + if (!node) + return NULL; + return (struct syscommon_resman_resource_driver *)node->data; +} + +void +syscommon_resman_add_resource_driver(const struct syscommon_resman_resource_driver *driver) +{ + if (!driver) + return; + + g_resource_driver_head = + g_list_append(g_resource_driver_head, (gpointer)driver); +} + +void +syscommon_resman_remove_resource_driver(const struct syscommon_resman_resource_driver *driver) +{ + if (!driver) + return; + + g_resource_driver_head = + g_list_remove(g_resource_driver_head, (gpointer)driver); +} + +static void do_delete_resource(struct syscommon_resman_resource *resource) +{ + if (!resource->name) + free(resource->name); + if (!resource->attrs_value) + free(resource->attrs_value); + resource->attrs = NULL; + resource->num_attrs = 0; + + free(resource); +} + +void +syscommon_resman_delete_resource(struct syscommon_resman_resource *resource) +{ + if (!resource) + return; + + if (resource->driver && resource->driver->ops.delete) + resource->driver->ops.delete(resource); + + syscommon_resman_unset_resource_attr_interest(resource, RESOURCE_ATTR_MASK); + + do_delete_resource(resource); +} + +int +syscommon_resman_create_resource(struct syscommon_resman_resource **res, + int resource_type) +{ + const struct syscommon_resman_resource_driver *driver = NULL; + struct syscommon_resman_resource *resource = NULL; + int i, ret; + + if (!res) + return -EINVAL; + + driver = syscommon_resman_find_resource_driver(resource_type); + if (!driver) + return -EINVAL; + + resource = calloc(1, sizeof(*resource)); + if (!resource) + return -ENOMEM; + + resource->type = resource_type; + resource->name = g_strdup(driver->name); + resource->driver = driver; + resource->num_attrs = driver->num_attrs; + resource->attrs = driver->attrs; + resource->attrs_value = calloc(resource->num_attrs, + sizeof(*resource->attrs_value)); + if (!resource->attrs_value) { + do_delete_resource(resource); + return -ENOMEM; + } + + for (i = 0; i < resource->num_attrs; i++) + resource->attrs_value[i].type = driver->attrs[i].type; + + resource->ctrls = driver->ctrls; + resource->num_ctrls = driver->num_ctrls; + resource->flag = SYSCOMMON_RESMAN_RESOURCE_FLAG_PRIVATE; + + if (driver->ops.create) { + ret = driver->ops.create(resource); + if (ret < 0) { + do_delete_resource(resource); + return ret; + } + } + + *res = resource; + + return 0; +} + +int +syscommon_resman_set_resource_flag(struct syscommon_resman_resource *resource, + u_int64_t flag_mask) +{ + if (!resource) + return -EINVAL; + + resource->flag = flag_mask; + return 0; +} + +int +syscommon_resman_set_resource_control(struct syscommon_resman_resource *resource, + u_int64_t ctrl_id, const void *data) +{ + const struct syscommon_resman_resource_control *ctrl; + int ctrl_index = RESOURCE_CTRL_INDEX(ctrl_id); + int ret; + + if (!resource || ctrl_index < 0 || ctrl_index >= resource->num_ctrls) + return -EINVAL; + + ctrl = &resource->ctrls[ctrl_index]; + if (!ctrl->ops.set) + return -ENOTSUP; + + ret = ctrl->ops.set(resource, ctrl, data); + if (ret < 0) + return ret; + + return 0; +} + +const char * +syscommon_resman_get_resource_control_name(struct syscommon_resman_resource *resource, + u_int64_t ctrl_id) +{ + const struct syscommon_resman_resource_control *ctrl; + int ctrl_index = RESOURCE_CTRL_INDEX(ctrl_id); + + if (!resource || ctrl_index < 0 || ctrl_index >= resource->num_ctrls) + return NULL; + + ctrl = &resource->ctrls[ctrl_index]; + + return ctrl->name; +} + +static int +update_resource_attr(struct syscommon_resman_resource *resource, + u_int64_t attr_id) +{ + int attr_index = RESOURCE_ATTR_INDEX(attr_id); + const struct syscommon_resman_resource_attribute *attr = NULL; + struct syscommon_resman_resource_attribute_value *attr_value = NULL; + int ret; + + if (!resource || attr_index < 0 || attr_index >= resource->num_attrs) + return -EINVAL; + + attr = &resource->attrs[attr_index]; + + if (!attr->ops.get) + return -EINVAL; + + attr_value = &resource->attrs_value[attr_index]; + + ret = attr->ops.get(resource, attr, attr_value->data); + if (ret < 0) + return ret; + + return 0; +} + +int +syscommon_resman_update_resource_attrs(struct syscommon_resman_resource *resource) +{ + int i, ret; + + if (!resource || !resource->type) + return -EINVAL; + + if (resource->driver && resource->driver->ops.prepare_update) { + ret = resource->driver->ops.prepare_update(resource); + if (ret < 0) + return ret; + } + + for (i = 0; i < resource->num_attrs; i++) { + if (!(resource->attrs[i].id & resource->attr_interest)) + continue; + update_resource_attr(resource, resource->attrs[i].id); + } + + return 0; +} + +const struct syscommon_resman_resource_attribute * +syscommon_resman_get_resource_attr(struct syscommon_resman_resource *resource, + u_int64_t attr_id) +{ + int attr_index = RESOURCE_ATTR_INDEX(attr_id); + + if (!resource || attr_index < 0 || attr_index >= resource->num_attrs) + return NULL; + + return &resource->attrs[attr_index]; +} + +struct syscommon_resman_resource_attribute_value * +syscommon_resman_get_resource_attr_value(struct syscommon_resman_resource *resource, + u_int64_t attr_id) +{ + int attr_index = RESOURCE_ATTR_INDEX(attr_id); + + if (!resource || attr_index < 0 || attr_index >= resource->num_attrs) + return NULL; + + return &resource->attrs_value[attr_index]; +} + +int +syscommon_resman_is_resource_attr_supported(struct syscommon_resman_resource *resource, + u_int64_t attr_id, bool *supported) +{ + const struct syscommon_resman_resource_attribute *attr = NULL; + int attr_index = RESOURCE_ATTR_INDEX(attr_id); + int ret; + bool is_supported = false; + + if (!resource || attr_index < 0 || attr_index >= resource->num_attrs) + return -EINVAL; + + attr = &resource->attrs[attr_index]; + + if (attr->id & resource->attr_supported) { + is_supported = true; + } else if (attr->ops.is_supported) { + is_supported = attr->ops.is_supported(resource, attr); + } else if (attr->ops.get) { + /* + * Optionally, if .is_supported ops is not implemented, + * use .get ops in order to check whether the resource attribute + * is supported or not. + */ + char data[SYSCOMMON_RESMAN_BUFF_MAX] = {0, }; + + ret = attr->ops.get(resource, attr, (void *)data); + is_supported = (ret < 0) ? false : true; + } + + if (is_supported) + resource->attr_supported |= attr->id; + + *supported = is_supported; + + return 0; +} + +static bool +check_attr_validate(struct syscommon_resman_resource *resource, + u_int64_t attr_id, int type) +{ + const struct syscommon_resman_resource_attribute *attr; + + attr = syscommon_resman_get_resource_attr(resource, attr_id); + if (!attr) + return false; + + if (attr->type != type) + return false; + + if (!(attr->id & resource->attr_interest)) + return false; + + return true; +} + +static json_object * +_get_resource_attr_json(const struct syscommon_resman_resource_attribute *attr, + const struct syscommon_resman_resource_attribute_value *attr_value) +{ + json_object *jobj_attr, *jobj_attr_name, *jobj_attr_type, *jobj_attr_value, *jobj_temp; + struct syscommon_resman_array_value *array; + int i; + + switch (attr->type) { + case SYSCOMMON_RESMAN_DATA_TYPE_INT: + jobj_attr_value = json_object_new_int(*((int32_t *)attr_value->data)); + break; + case SYSCOMMON_RESMAN_DATA_TYPE_UINT: + jobj_attr_value = json_object_new_int(*((u_int32_t *)attr_value->data)); + break; + case SYSCOMMON_RESMAN_DATA_TYPE_INT64: + jobj_attr_value = json_object_new_int64(*((int64_t *)attr_value->data)); + break; + case SYSCOMMON_RESMAN_DATA_TYPE_UINT64: + jobj_attr_value = json_object_new_uint64(*((u_int64_t *)attr_value->data)); + break; + case SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE: + jobj_attr_value = json_object_new_double(*((double *)attr_value->data)); + break; + case SYSCOMMON_RESMAN_DATA_TYPE_STRING: + jobj_attr_value = json_object_new_string((char *)attr_value->data); + break; + case SYSCOMMON_RESMAN_DATA_TYPE_ARRAY: + array = attr_value->data; + + jobj_attr_value = json_object_new_array(); + + switch (array->type) { + case SYSCOMMON_RESMAN_DATA_TYPE_INT: + for (i = 0; i < array->length; i++) { + int32_t *item = array->data; + + jobj_temp = json_object_new_int(item[i]); + json_object_array_add(jobj_attr_value, jobj_temp); + } + break; + case SYSCOMMON_RESMAN_DATA_TYPE_UINT: + for (i = 0; i < array->length; i++) { + u_int32_t *item = array->data; + + jobj_temp = json_object_new_int(item[i]); + json_object_array_add(jobj_attr_value, jobj_temp); + } + break; + case SYSCOMMON_RESMAN_DATA_TYPE_INT64: + for (i = 0; i < array->length; i++) { + int64_t *item = array->data; + + jobj_temp = json_object_new_int64(item[i]); + json_object_array_add(jobj_attr_value, jobj_temp); + } + break; + case SYSCOMMON_RESMAN_DATA_TYPE_UINT64: + for (i = 0; i < array->length; i++) { + u_int64_t *item = array->data; + + jobj_temp = json_object_new_uint64(item[i]); + json_object_array_add(jobj_attr_value, jobj_temp); + } + break; + case SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE: + for (i = 0; i < array->length; i++) { + double *item = array->data; + + jobj_temp = json_object_new_double(item[i]); + json_object_array_add(jobj_attr_value, jobj_temp); + } + break; + case SYSCOMMON_RESMAN_DATA_TYPE_STRING: + for (i = 0; i < array->length; i++) { + char **item = array->data; + + jobj_temp = json_object_new_string(item[i]); + json_object_array_add(jobj_attr_value, jobj_temp); + } + break; + default: + json_object_put(jobj_attr_value); + jobj_attr_value = json_object_new_null(); + } + break; + default: + jobj_attr_value = json_object_new_null(); + } + jobj_attr = json_object_new_object(); + + jobj_attr_name = json_object_new_string(attr->name); + /* + * Since actually JSON format has no data type limitation itself, in many + * cases except converting JSON into json-c, type is not required. So, + * for the case of converting JSON generated here to json-c object again + * json_type is stored in each attributes. + */ + jobj_attr_type = json_object_new_int(json_object_get_type(jobj_attr_value)); + + json_object_object_add(jobj_attr, "name", jobj_attr_name); + json_object_object_add(jobj_attr, "json_type", jobj_attr_type); + json_object_object_add(jobj_attr, "value", jobj_attr_value); + + return jobj_attr; +} + +static void _put_resource_attr_json(json_object *jobj_attr) +{ + json_object *jobj_attr_value; + + json_object_object_del(jobj_attr, "name"); + json_object_object_del(jobj_attr, "json_type"); + + if (json_object_object_get_ex(jobj_attr, "value", &jobj_attr_value) && + json_object_is_type(jobj_attr_value, json_type_array)) + json_object_array_del_idx(jobj_attr_value, 0, + json_object_array_length(jobj_attr_value)); + + json_object_object_del(jobj_attr, "value"); +} + +int +syscommon_resman_get_resource_attrs_json(struct syscommon_resman_resource *resource, + char **json_string) +{ + json_object *jobj_root, *jobj_res_name, *jobj_res_type, *jobj_res_attrs, *jobj_attr; + const struct syscommon_resman_resource_attribute *attr; + const struct syscommon_resman_resource_attribute_value *attr_value; + int i; + + if (!resource || !resource->type) + return -EINVAL; + + jobj_root = json_object_new_object(); + + jobj_res_name = json_object_new_string(resource->name); + jobj_res_type = json_object_new_int(resource->type); + jobj_res_attrs = json_object_new_array(); + + for (i = 0; i < resource->num_attrs; i++) { + if (!(resource->attrs[i].id & resource->attr_interest)) + continue; + + attr = &resource->attrs[i]; + attr_value = &resource->attrs_value[i]; + + jobj_attr = _get_resource_attr_json(attr, attr_value); + + json_object_array_add(jobj_res_attrs, jobj_attr); + } + + json_object_object_add(jobj_root, "res_name", jobj_res_name); + json_object_object_add(jobj_root, "res_type", jobj_res_type); + json_object_object_add(jobj_root, "res_attrs", jobj_res_attrs); + + *json_string = strdup(json_object_to_json_string(jobj_root)); + + for (i = 0; i < json_object_array_length(jobj_res_attrs); i++) { + jobj_attr = json_object_array_get_idx(jobj_res_attrs, i); + _put_resource_attr_json(jobj_attr); + } + json_object_array_del_idx(jobj_res_attrs, 0, json_object_array_length(jobj_res_attrs)); + + json_object_object_del(jobj_root, "res_attrs"); + json_object_object_del(jobj_root, "res_type"); + json_object_object_del(jobj_root, "res_name"); + json_object_put(jobj_root); + + return 0; +} + +int +syscommon_resman_get_resource_attr_json(struct syscommon_resman_resource *resource, + u_int64_t attr_id, char **json_string) +{ + const struct syscommon_resman_resource_attribute *attr; + const struct syscommon_resman_resource_attribute_value *attr_value; + json_object *jobj_attr; + + attr = syscommon_resman_get_resource_attr(resource, attr_id); + attr_value = syscommon_resman_get_resource_attr_value(resource, attr_id); + + if (!attr || !attr_value) + return -EINVAL; + + jobj_attr = _get_resource_attr_json(attr, attr_value); + + *json_string = strdup(json_object_to_json_string(jobj_attr)); + + _put_resource_attr_json(jobj_attr); + + return 0; +} + +static json_object * +get_resource_driver_json(const struct syscommon_resman_resource_driver *driver) +{ + json_object *jobj_drv, *jobj_drv_name, *jobj_drv_type; + json_object *jobj_drv_attrs_array, *jobj_drv_attr, *jobj_drv_ctrls_array, *jobj_drv_ctrl; + json_object *jobj_drv_attr_name, *jobj_drv_attr_type, *jobj_drv_attr_id; + json_object *jobj_drv_ctrl_name, *jobj_drv_ctrl_id; + const struct syscommon_resman_resource_attribute *attr; + const struct syscommon_resman_resource_control *ctrl; + int i; + + jobj_drv = json_object_new_object(); + + jobj_drv_name = json_object_new_string(driver->name); + jobj_drv_type = json_object_new_int(driver->type); + + jobj_drv_attrs_array = json_object_new_array(); + + for (i = 0; i < driver->num_attrs; i++) { + attr = &driver->attrs[i]; + + jobj_drv_attr = json_object_new_object(); + + jobj_drv_attr_name = json_object_new_string(attr->name); + jobj_drv_attr_type = json_object_new_int(attr->type); + jobj_drv_attr_id = json_object_new_uint64(attr->id); + + json_object_object_add(jobj_drv_attr, "name", jobj_drv_attr_name); + json_object_object_add(jobj_drv_attr, "type", jobj_drv_attr_type); + json_object_object_add(jobj_drv_attr, "id", jobj_drv_attr_id); + + json_object_array_add(jobj_drv_attrs_array, jobj_drv_attr); + } + + jobj_drv_ctrls_array = json_object_new_array(); + + for (i = 0; i < driver->num_ctrls; i++) { + ctrl = &driver->ctrls[i]; + + jobj_drv_ctrl = json_object_new_object(); + + jobj_drv_ctrl_name = json_object_new_string(ctrl->name); + jobj_drv_ctrl_id = json_object_new_uint64(ctrl->id); + + json_object_object_add(jobj_drv_ctrl, "name", jobj_drv_ctrl_name); + json_object_object_add(jobj_drv_ctrl, "id", jobj_drv_ctrl_id); + + json_object_array_add(jobj_drv_ctrls_array, jobj_drv_ctrl); + } + + json_object_object_add(jobj_drv, "name", jobj_drv_name); + json_object_object_add(jobj_drv, "type", jobj_drv_type); + json_object_object_add(jobj_drv, "attrs", jobj_drv_attrs_array); + json_object_object_add(jobj_drv, "ctrls", jobj_drv_ctrls_array); + + return jobj_drv; +} + +static void put_resource_driver_json(json_object *jobj_drv) +{ + json_object *jobj_array, *jobj_obj; + int i; + + if (json_object_object_get_ex(jobj_drv, "ctrls", &jobj_array)) { + for (i = 0; i < json_object_array_length(jobj_array); i++) { + jobj_obj = json_object_array_get_idx(jobj_array, i); + + json_object_object_del(jobj_obj, "id"); + json_object_object_del(jobj_obj, "name"); + } + json_object_array_del_idx(jobj_array, 0, json_object_array_length(jobj_array)); + } + + if (json_object_object_get_ex(jobj_drv, "attrs", &jobj_array)) { + for (i = 0; i < json_object_array_length(jobj_array); i++) { + jobj_obj = json_object_array_get_idx(jobj_array, i); + + json_object_object_del(jobj_obj, "id"); + json_object_object_del(jobj_obj, "type"); + json_object_object_del(jobj_obj, "name"); + } + json_object_array_del_idx(jobj_array, 0, json_object_array_length(jobj_array)); + } + + json_object_object_del(jobj_drv, "ctrls"); + json_object_object_del(jobj_drv, "attrs"); + json_object_object_del(jobj_drv, "type"); + json_object_object_del(jobj_drv, "name"); +} + +int syscommon_resman_get_resource_list_json(char **json_string) +{ + const struct syscommon_resman_resource_driver *driver; + json_object *jobj_root, *jobj_drvs_array, *jobj_drv; + int i; + + jobj_root = json_object_new_object(); + jobj_drvs_array = json_object_new_array(); + + for (i = 0; i < g_list_length(g_resource_driver_head); i++) { + driver = g_list_nth(g_list_first(g_resource_driver_head), i)->data; + + jobj_drv = get_resource_driver_json(driver); + json_object_array_add(jobj_drvs_array, jobj_drv); + } + + json_object_object_add(jobj_root, "resources", jobj_drvs_array); + + *json_string = strdup(json_object_to_json_string(jobj_root)); + + for (i = 0; i < json_object_array_length(jobj_drvs_array); i++) { + jobj_drv = json_object_array_get_idx(jobj_drvs_array, i); + + put_resource_driver_json(jobj_drv); + } + json_object_array_del_idx(jobj_drvs_array, 0, json_object_array_length(jobj_drvs_array)); + + json_object_object_del(jobj_root, "resources"); + json_object_put(jobj_root); + + return 0; +} + +int +syscommon_resman_get_resource_attr_int(struct syscommon_resman_resource *resource, + u_int64_t attr_id, int32_t *data) +{ + struct syscommon_resman_resource_attribute_value *attr_value = NULL; + + if (!check_attr_validate(resource, attr_id, SYSCOMMON_RESMAN_DATA_TYPE_INT)) + return -EINVAL; + + attr_value = syscommon_resman_get_resource_attr_value(resource, attr_id); + if (!attr_value) + return -EINVAL; + + *data = *((int32_t *)attr_value->data); + + return 0; +} + +int +syscommon_resman_get_resource_attr_int64(struct syscommon_resman_resource *resource, + u_int64_t attr_id, int64_t *data) +{ + struct syscommon_resman_resource_attribute_value *attr_value = NULL; + + if (!check_attr_validate(resource, attr_id, SYSCOMMON_RESMAN_DATA_TYPE_INT64)) + return -EINVAL; + + attr_value = syscommon_resman_get_resource_attr_value(resource, attr_id); + if (!attr_value) + return -EINVAL; + + *data = *((int64_t *)attr_value->data); + + return 0; +} + +int +syscommon_resman_get_resource_attr_uint(struct syscommon_resman_resource *resource, + u_int64_t attr_id, u_int32_t *data) +{ + struct syscommon_resman_resource_attribute_value *attr_value = NULL; + + if (!check_attr_validate(resource, attr_id, SYSCOMMON_RESMAN_DATA_TYPE_UINT)) + return -EINVAL; + + attr_value = syscommon_resman_get_resource_attr_value(resource, attr_id); + if (!attr_value) + return -EINVAL; + + *data = *((u_int32_t *)attr_value->data); + + return 0; +} + +int +syscommon_resman_get_resource_attr_uint64(struct syscommon_resman_resource *resource, + u_int64_t attr_id, u_int64_t *data) +{ + struct syscommon_resman_resource_attribute_value *attr_value = NULL; + + if (!check_attr_validate(resource, attr_id, SYSCOMMON_RESMAN_DATA_TYPE_UINT64)) + return -EINVAL; + + attr_value = syscommon_resman_get_resource_attr_value(resource, attr_id); + if (!attr_value) + return -EINVAL; + + *data = *((u_int64_t *)attr_value->data); + + return 0; +} + +int +syscommon_resman_get_resource_attr_double(struct syscommon_resman_resource *resource, + u_int64_t attr_id, double *data) +{ + struct syscommon_resman_resource_attribute_value *attr_value = NULL; + + if (!check_attr_validate(resource, attr_id, SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE)) + return -EINVAL; + + attr_value = syscommon_resman_get_resource_attr_value(resource, attr_id); + if (!attr_value) + return -EINVAL; + + *data = *((double *)attr_value->data); + + return 0; +} + +int +syscommon_resman_get_resource_attr_string(struct syscommon_resman_resource *resource, + u_int64_t attr_id, char *data) +{ + struct syscommon_resman_resource_attribute_value *attr_value = NULL; + + if (!check_attr_validate(resource, attr_id, SYSCOMMON_RESMAN_DATA_TYPE_STRING)) + return -EINVAL; + + attr_value = syscommon_resman_get_resource_attr_value(resource, attr_id); + if (!attr_value) + return -EINVAL; + + strncpy(data, (char *)attr_value->data, SYSCOMMON_RESMAN_BUFF_MAX); + + return 0; +} + +int +syscommon_resman_get_resource_attr_array(struct syscommon_resman_resource *resource, + u_int64_t attr_id, + struct syscommon_resman_array_value **data) +{ + struct syscommon_resman_resource_attribute_value *attr_value = NULL; + + if (!check_attr_validate(resource, attr_id, SYSCOMMON_RESMAN_DATA_TYPE_ARRAY)) + return -EINVAL; + + attr_value = syscommon_resman_get_resource_attr_value(resource, attr_id); + if (!attr_value) + return -EINVAL; + + *data = (struct syscommon_resman_array_value *)attr_value->data; + + return 0; +} + +int +syscommon_resman_get_resource_attr_ptr(struct syscommon_resman_resource *resource, + u_int64_t attr_id, void **data) +{ + struct syscommon_resman_resource_attribute_value *attr_value = NULL; + + if (!check_attr_validate(resource, attr_id, SYSCOMMON_RESMAN_DATA_TYPE_PTR)) + return -EINVAL; + + attr_value = syscommon_resman_get_resource_attr_value(resource, attr_id); + if (!attr_value) + return -EINVAL; + + *data = attr_value->data; + + return 0; +} + +static inline bool +is_resource_attr_visible(struct syscommon_resman_resource *resource, + const struct syscommon_resman_resource_attribute *attr) +{ + u_int64_t res_visibility, attr_visibility; + + res_visibility = resource->flag & RESOURCE_FLAG_VISIBILITY_MASK; + attr_visibility = attr->flag & RESOURCE_ATTR_FLAG_VISIBILITY_MASK; + + /* bigger visibility means smaller privilege */ + if (res_visibility > attr_visibility) + return false; + + return true; +} + +int +syscommon_resman_set_resource_attr_interest(struct syscommon_resman_resource *resource, + u_int64_t interest_mask) +{ + struct syscommon_resman_resource_attribute_value *attr_value; + int i, ret; + bool supported; + + if (!resource) + return -EINVAL; + + for (i = 0; i < resource->num_attrs; i++) { + if (!(resource->attrs[i].id & interest_mask)) + continue; + + ret = syscommon_resman_is_resource_attr_supported(resource, + resource->attrs[i].id, + &supported); + if (ret < 0) { + goto err; + } else if (!supported) { + ret = -ENOTSUP; + goto err; + } + + if (!is_resource_attr_visible(resource, &resource->attrs[i])) { + ret = -EACCES; + goto err; + } + + attr_value = syscommon_resman_get_resource_attr_value(resource, + resource->attrs[i].id); + if (!attr_value) { + ret = -EINVAL; + goto err; + } + + /* + * In resource monitor, each resource has a lot of attributes, but + * only updated attributes are selected by clients on demand. So, + * instead of allocating memory at the resource creation, allocate + * at the set interest. + */ + if (!attr_value->data) { + switch (attr_value->type) { + case SYSCOMMON_RESMAN_DATA_TYPE_INT: + attr_value->data = calloc(1, sizeof(int32_t)); + break; + case SYSCOMMON_RESMAN_DATA_TYPE_INT64: + attr_value->data = calloc(1, sizeof(int64_t)); + break; + case SYSCOMMON_RESMAN_DATA_TYPE_UINT: + attr_value->data = calloc(1, sizeof(u_int32_t)); + break; + case SYSCOMMON_RESMAN_DATA_TYPE_UINT64: + attr_value->data = calloc(1, sizeof(u_int64_t)); + break; + case SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE: + attr_value->data = calloc(1, sizeof(double)); + break; + case SYSCOMMON_RESMAN_DATA_TYPE_STRING: + attr_value->data = calloc(SYSCOMMON_RESMAN_BUFF_MAX, sizeof(char)); + break; + case SYSCOMMON_RESMAN_DATA_TYPE_ARRAY: + attr_value->data = calloc(1, sizeof(struct syscommon_resman_array_value)); + break; + default: + ret = -EINVAL; + goto err; + } + + if (!attr_value->data) { + ret = -ENOMEM; + goto err; + } + } + } + + resource->attr_interest |= interest_mask; + + return 0; + +err: + for (; i >= 0; i--) { + if (!(resource->attrs[i].id & interest_mask)) + continue; + + attr_value = syscommon_resman_get_resource_attr_value(resource, + resource->attrs[i].id); + if (!attr_value) + continue; + + if (attr_value->data) { + free(attr_value->data); + attr_value->data = NULL; + } + } + + return ret; +} + +int +syscommon_resman_unset_resource_attr_interest(struct syscommon_resman_resource *resource, + u_int64_t interest_mask) +{ + struct syscommon_resman_resource_attribute_value *attr_value; + int i; + + if (!resource) + return -EINVAL; + + if (!syscommon_resman_is_resource_attr_interested(resource, interest_mask)) + return -EINVAL; + + for (i = 0; i < resource->num_attrs; i++) { + if (!(resource->attrs[i].id & interest_mask)) + continue; + + attr_value = syscommon_resman_get_resource_attr_value(resource, + resource->attrs[i].id); + if (!attr_value) + return -EINVAL; + + if (attr_value->data) { + switch (attr_value->type) { + case SYSCOMMON_RESMAN_DATA_TYPE_ARRAY: + { + struct syscommon_resman_array_value *array = attr_value->data; + + if (array->data) { + free(array->data); + array->data = NULL; + } + /* fall through */ + } + case SYSCOMMON_RESMAN_DATA_TYPE_INT: + case SYSCOMMON_RESMAN_DATA_TYPE_INT64: + case SYSCOMMON_RESMAN_DATA_TYPE_UINT: + case SYSCOMMON_RESMAN_DATA_TYPE_UINT64: + case SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE: + case SYSCOMMON_RESMAN_DATA_TYPE_STRING: + free(attr_value->data); + attr_value->data = NULL; + break; + default: + return -EINVAL; + } + } + } + + resource->attr_interest &= ~interest_mask; + + return 0; +} + +bool +syscommon_resman_is_resource_attr_interested(struct syscommon_resman_resource *resource, + u_int64_t interest_mask) +{ + if (!resource) + return false; + + if (resource->attr_interest != (resource->attr_interest | interest_mask)) + return false; + + return true; +} + +const char * +syscommon_resman_get_resource_attr_name(struct syscommon_resman_resource *resource, + u_int64_t attr_id) +{ + const struct syscommon_resman_resource_attribute *attr; + + attr = syscommon_resman_get_resource_attr(resource, attr_id); + if (!attr) + return NULL; + + return attr->name; +} + +const char *syscommon_resman_get_resource_name(struct syscommon_resman_resource *resource) +{ + return resource ? resource->name : NULL; +} + +void *syscommon_resman_get_resource_privdata(struct syscommon_resman_resource *resource) +{ + return resource ? resource->priv : NULL; +} + +int syscommon_resman_get_resource_type(struct syscommon_resman_resource *resource) +{ + return resource ? resource->type : -EINVAL; +} + +int +syscommon_resman_set_resource_privdata(struct syscommon_resman_resource *resource, + void *priv) +{ + if (!resource) + return -EINVAL; + + resource->priv = priv; + + return 0; +} + +void syscommon_resman_init_resource_drivers(void) +{ + struct syscommon_resman_resource_driver *driver; + int i, ret = 0; + + for (i = 0; i < g_list_length(g_resource_driver_head); i++) { + driver = g_list_nth(g_list_first(g_resource_driver_head), i)->data; + + if (driver->ops.init) { + ret = driver->ops.init(); + if (ret < 0) + syscommon_resman_remove_resource_driver(driver); + } + } +} + +void syscommon_resman_exit_resource_drivers(void) +{ + const struct syscommon_resman_resource_driver *driver; + int i; + + for (i = 0; i < g_list_length(g_resource_driver_head); i++) { + driver = g_list_nth(g_list_first(g_resource_driver_head), i)->data; + + if (driver->ops.exit) + driver->ops.exit(); + } +} diff --git a/src/resource-manager/resource-manager.h b/src/resource-manager/resource-manager.h new file mode 100644 index 0000000..f070dd5 --- /dev/null +++ b/src/resource-manager/resource-manager.h @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __RESOURCE_MANAGER_H__ +#define __RESOURCE_MANAGER_H__ + +// common.h + +#include +#include +#include +#include + +#define SYSCOMMON_RESMAN_BUFF_MAX 255 + +struct syscommon_resman_resource; +struct syscommon_resman_resource_attribute; +struct syscommon_resman_resource_control; + +struct syscommon_resman_array_value { + int type; + int length; + void *data; +}; + +struct syscommon_resman_resource_attribute_value { + int type; + void *data; +}; + +struct syscommon_resman_resource_attribute_ops { + int (*set)(struct syscommon_resman_resource *res, + const struct syscommon_resman_resource_attribute *attr, + const void *data, int count); + int (*get)(struct syscommon_resman_resource *res, + const struct syscommon_resman_resource_attribute *attr, + void *data); + /* + * If .is_supported ops is not implemented, use .get ops in order to + * check whether the resource attribute is supported or not. + */ + bool (*is_supported)(struct syscommon_resman_resource *res, + const struct syscommon_resman_resource_attribute *attr); +}; + +struct syscommon_resman_resource_attribute { + const char name[SYSCOMMON_RESMAN_BUFF_MAX]; + const u_int64_t id; + const int type; + const u_int64_t flag; + const struct syscommon_resman_resource_attribute_ops ops; +}; + +struct syscommon_resman_resource_control_ops { + const int (*set)(struct syscommon_resman_resource *res, + const struct syscommon_resman_resource_control *ctrl, + const void *data); + const int (*get)(struct syscommon_resman_resource *res, + const struct syscommon_resman_resource_control *ctrl, + void **data); +}; + +struct syscommon_resman_resource_control { + const char name[SYSCOMMON_RESMAN_BUFF_MAX]; + const u_int64_t id; + const struct syscommon_resman_resource_control_ops ops; +}; + +struct syscommon_resman_resource_driver_ops { + int (*init)(void); + void (*exit)(void); + int (*create)(struct syscommon_resman_resource *res); + void (*delete)(struct syscommon_resman_resource *res); + /* + * If prepare_update is specified, it will be called + * at every update_resource_attrs(). + */ + int (*prepare_update)(struct syscommon_resman_resource *res); +}; + +struct syscommon_resman_resource_driver { + const char *name; + const int type; + const u_int64_t flag; + const int num_attrs; + const struct syscommon_resman_resource_attribute *attrs; + const int num_ctrls; + const struct syscommon_resman_resource_control *ctrls; + const struct syscommon_resman_resource_driver_ops ops; +}; + +struct syscommon_resman_resource { + char *name; + const struct syscommon_resman_resource_driver *driver; + int type; + + int num_attrs; + const struct syscommon_resman_resource_attribute *attrs; + struct syscommon_resman_resource_attribute_value *attrs_value; + int num_ctrls; + const struct syscommon_resman_resource_control *ctrls; + + u_int64_t flag; + + void *priv; + + u_int64_t attr_interest; + u_int64_t attr_supported; +}; + +#define SYSCOMMON_RESMAN_RESOURCE_DRIVER_REGISTER(resource_driver) \ +static void __attribute__((constructor)) __syscommon_resman_module_init(void) \ +{ \ + syscommon_resman_add_resource_driver(resource_driver); \ +} \ +static void __attribute__((destructor)) __syscommon_resman_module_exit(void) \ +{ \ + syscommon_resman_remove_resource_driver(resource_driver); \ +} + +/* Add/remove resource driver and device */ +const struct syscommon_resman_resource_driver * +syscommon_resman_find_resource_driver(int resource_type); +void syscommon_resman_add_resource_driver(const struct syscommon_resman_resource_driver *resource_driver); +void syscommon_resman_remove_resource_driver(const struct syscommon_resman_resource_driver *resource_driver); + +/* Create/delete resource instance */ +int syscommon_resman_create_resource(struct syscommon_resman_resource **res, int resource_type); +void syscommon_resman_delete_resource(struct syscommon_resman_resource *resource); + +/* Set flag of the resource to given flag mask */ +int syscommon_resman_set_resource_flag(struct syscommon_resman_resource *resource, u_int64_t flag_mask); + +/* Handle resource control */ +int syscommon_resman_set_resource_control(struct syscommon_resman_resource *resource, u_int64_t ctrl_id, const void *data); +const char *syscommon_resman_get_resource_control_name(struct syscommon_resman_resource *resource, u_int64_t ctrl_id); + +/* Handle resource attribute */ +int syscommon_resman_update_resource_attrs(struct syscommon_resman_resource *resource); +const struct syscommon_resman_resource_attribute * +syscommon_resman_get_resource_attr(struct syscommon_resman_resource *resource, u_int64_t attr_id); +struct syscommon_resman_resource_attribute_value * +syscommon_resman_get_resource_attr_value(struct syscommon_resman_resource *resource, u_int64_t attr_id); +int syscommon_resman_is_resource_attr_supported(struct syscommon_resman_resource *resource, u_int64_t attr_id, bool *supported); + +static inline bool +syscommon_resman_resource_attr_supported_always(struct syscommon_resman_resource *resource, + const struct syscommon_resman_resource_attribute *attr) +{ + return true; +} + +int syscommon_resman_get_resource_attrs_json(struct syscommon_resman_resource *resource, char **json_string); +int syscommon_resman_get_resource_attr_json(struct syscommon_resman_resource *resource, u_int64_t attr_id, char **json_string); +int syscommon_resman_get_resource_list_json(char **json_string); + +int syscommon_resman_get_resource_attr_int(struct syscommon_resman_resource *resource, u_int64_t attr_id, int32_t *data); +int syscommon_resman_get_resource_attr_int64(struct syscommon_resman_resource *resource, u_int64_t attr_id, int64_t *data); +int syscommon_resman_get_resource_attr_uint(struct syscommon_resman_resource *resource, u_int64_t attr_id, u_int32_t *data); +int syscommon_resman_get_resource_attr_uint64(struct syscommon_resman_resource *resource, u_int64_t attr_id, u_int64_t *data); +int syscommon_resman_get_resource_attr_double(struct syscommon_resman_resource *resource, u_int64_t attr_id, double *data); +int syscommon_resman_get_resource_attr_string(struct syscommon_resman_resource *resource, u_int64_t attr_id, char *data); +int syscommon_resman_get_resource_attr_array(struct syscommon_resman_resource *resource, u_int64_t attr_id, + struct syscommon_resman_array_value **data); +int syscommon_resman_get_resource_attr_ptr(struct syscommon_resman_resource *resource, u_int64_t attr_id, void **data); + +int syscommon_resman_set_resource_attr_interest(struct syscommon_resman_resource *resource, u_int64_t interest_mask); +int syscommon_resman_unset_resource_attr_interest(struct syscommon_resman_resource *resource, u_int64_t interest_mask); +bool syscommon_resman_is_resource_attr_interested(struct syscommon_resman_resource *resource, u_int64_t interest_mask); +const char *syscommon_resman_get_resource_attr_name(struct syscommon_resman_resource *resource, u_int64_t attr_id); + +const char *syscommon_resman_get_resource_name(struct syscommon_resman_resource *resource); +void *syscommon_resman_get_resource_privdata(struct syscommon_resman_resource *resource); +int syscommon_resman_get_resource_type(struct syscommon_resman_resource *resource); + +int syscommon_resman_set_resource_privdata(struct syscommon_resman_resource *resource, void *priv); + +void syscommon_resman_init_resource_drivers(void); +void syscommon_resman_exit_resource_drivers(void); +#endif diff --git a/src/resource-manager/resource-type.h b/src/resource-manager/resource-type.h new file mode 100644 index 0000000..2ee7b38 --- /dev/null +++ b/src/resource-manager/resource-type.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#ifndef __RESOURCE_TYPE_H__ +#define __RESOURCE_TYPE_H__ + +enum syscommon_resman_data_type { + SYSCOMMON_RESMAN_DATA_TYPE_UNKNOWN = 0, + SYSCOMMON_RESMAN_DATA_TYPE_INT, + SYSCOMMON_RESMAN_DATA_TYPE_INT64, + SYSCOMMON_RESMAN_DATA_TYPE_UINT, + SYSCOMMON_RESMAN_DATA_TYPE_UINT64, + SYSCOMMON_RESMAN_DATA_TYPE_DOUBLE, + SYSCOMMON_RESMAN_DATA_TYPE_STRING, + SYSCOMMON_RESMAN_DATA_TYPE_ARRAY, + SYSCOMMON_RESMAN_DATA_TYPE_PTR, + SYSCOMMON_RESMAN_DATA_TYPE_BOOLEAN, + SYSCOMMON_RESMAN_DATA_TYPE_NUM +}; + +#define BIT(x) (1ULL << x) + +/* + * Resource driver flags + * - RESOURCE_DRIVER_FLAG_COUNT_ONLY_ONE indicates the unique device which + * presents only one. + * - RESOURCE_DRIVER_FLAG_UNCOUNTABLE incidates that resource which is not + * bound physical h/w. + */ +#define SYSCOMMON_RESMAN_RESOURCE_DRIVER_FLAG_COUNT_ONLY_ONE BIT(0) +#define SYSCOMMON_RESMAN_RESOURCE_DRIVER_FLAG_UNCOUNTABLE BIT(1) + +/* Resource flags */ +#define SYSCOMMON_RESMAN_RESOURCE_FLAG_PRIVATE BIT(0) +#define SYSCOMMON_RESMAN_RESOURCE_FLAG_PUBLIC BIT(1) + +/* Flags for resource_attribute */ +#define SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PRIVATE BIT(0) +#define SYSCOMMON_RESMAN_RESOURCE_ATTR_FLAG_PUBLIC BIT(1) + +#endif