162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Functions corresponding to enumeration type attributes under
462306a36Sopenharmony_ci * BIOS Enumeration GUID for use with hp-bioscfg driver.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2022 HP Development Company, L.P.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "bioscfg.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ciGET_INSTANCE_ID(enumeration);
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	int instance_id = get_enumeration_instance_id(kobj);
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci	if (instance_id < 0)
1862306a36Sopenharmony_ci		return -EIO;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n",
2162306a36Sopenharmony_ci			 bioscfg_drv.enumeration_data[instance_id].current_value);
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/**
2562306a36Sopenharmony_ci * validate_enumeration_input() -
2662306a36Sopenharmony_ci * Validate input of current_value against possible values
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * @instance_id: The instance on which input is validated
2962306a36Sopenharmony_ci * @buf: Input value
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_cistatic int validate_enumeration_input(int instance_id, const char *buf)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	int i;
3462306a36Sopenharmony_ci	int found = 0;
3562306a36Sopenharmony_ci	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	/* Is it a read only attribute */
3862306a36Sopenharmony_ci	if (enum_data->common.is_readonly)
3962306a36Sopenharmony_ci		return -EIO;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	for (i = 0; i < enum_data->possible_values_size && !found; i++)
4262306a36Sopenharmony_ci		if (!strcmp(enum_data->possible_values[i], buf))
4362306a36Sopenharmony_ci			found = 1;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (!found)
4662306a36Sopenharmony_ci		return -EINVAL;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	return 0;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic void update_enumeration_value(int instance_id, char *attr_value)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	strscpy(enum_data->current_value,
5662306a36Sopenharmony_ci		attr_value,
5762306a36Sopenharmony_ci		sizeof(enum_data->current_value));
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ciATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name, enumeration);
6162306a36Sopenharmony_cistatic struct kobj_attribute enumeration_display_name =
6262306a36Sopenharmony_ci		__ATTR_RO(display_name);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ciATTRIBUTE_PROPERTY_STORE(current_value, enumeration);
6562306a36Sopenharmony_cistatic struct kobj_attribute enumeration_current_val =
6662306a36Sopenharmony_ci		__ATTR_RW(current_value);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciATTRIBUTE_VALUES_PROPERTY_SHOW(possible_values, enumeration, SEMICOLON_SEP);
6962306a36Sopenharmony_cistatic struct kobj_attribute enumeration_poss_val =
7062306a36Sopenharmony_ci		__ATTR_RO(possible_values);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
7362306a36Sopenharmony_ci			 char *buf)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	return sysfs_emit(buf, "enumeration\n");
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic struct kobj_attribute enumeration_type =
7962306a36Sopenharmony_ci		__ATTR_RO(type);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic struct attribute *enumeration_attrs[] = {
8262306a36Sopenharmony_ci	&common_display_langcode.attr,
8362306a36Sopenharmony_ci	&enumeration_display_name.attr,
8462306a36Sopenharmony_ci	&enumeration_current_val.attr,
8562306a36Sopenharmony_ci	&enumeration_poss_val.attr,
8662306a36Sopenharmony_ci	&enumeration_type.attr,
8762306a36Sopenharmony_ci	NULL
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic const struct attribute_group enumeration_attr_group = {
9162306a36Sopenharmony_ci	.attrs = enumeration_attrs,
9262306a36Sopenharmony_ci};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ciint hp_alloc_enumeration_data(void)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	bioscfg_drv.enumeration_instances_count =
9762306a36Sopenharmony_ci		hp_get_instance_count(HP_WMI_BIOS_ENUMERATION_GUID);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	bioscfg_drv.enumeration_data = kcalloc(bioscfg_drv.enumeration_instances_count,
10062306a36Sopenharmony_ci					       sizeof(*bioscfg_drv.enumeration_data), GFP_KERNEL);
10162306a36Sopenharmony_ci	if (!bioscfg_drv.enumeration_data) {
10262306a36Sopenharmony_ci		bioscfg_drv.enumeration_instances_count = 0;
10362306a36Sopenharmony_ci		return -ENOMEM;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci	return 0;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/* Expected Values types associated with each element */
10962306a36Sopenharmony_cistatic const acpi_object_type expected_enum_types[] = {
11062306a36Sopenharmony_ci	[NAME] = ACPI_TYPE_STRING,
11162306a36Sopenharmony_ci	[VALUE] = ACPI_TYPE_STRING,
11262306a36Sopenharmony_ci	[PATH] = ACPI_TYPE_STRING,
11362306a36Sopenharmony_ci	[IS_READONLY] = ACPI_TYPE_INTEGER,
11462306a36Sopenharmony_ci	[DISPLAY_IN_UI] = ACPI_TYPE_INTEGER,
11562306a36Sopenharmony_ci	[REQUIRES_PHYSICAL_PRESENCE] = ACPI_TYPE_INTEGER,
11662306a36Sopenharmony_ci	[SEQUENCE] = ACPI_TYPE_INTEGER,
11762306a36Sopenharmony_ci	[PREREQUISITES_SIZE] = ACPI_TYPE_INTEGER,
11862306a36Sopenharmony_ci	[PREREQUISITES] = ACPI_TYPE_STRING,
11962306a36Sopenharmony_ci	[SECURITY_LEVEL] = ACPI_TYPE_INTEGER,
12062306a36Sopenharmony_ci	[ENUM_CURRENT_VALUE] = ACPI_TYPE_STRING,
12162306a36Sopenharmony_ci	[ENUM_SIZE] = ACPI_TYPE_INTEGER,
12262306a36Sopenharmony_ci	[ENUM_POSSIBLE_VALUES] = ACPI_TYPE_STRING,
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic int hp_populate_enumeration_elements_from_package(union acpi_object *enum_obj,
12662306a36Sopenharmony_ci							 int enum_obj_count,
12762306a36Sopenharmony_ci							 int instance_id)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	char *str_value = NULL;
13062306a36Sopenharmony_ci	int value_len;
13162306a36Sopenharmony_ci	u32 size = 0;
13262306a36Sopenharmony_ci	u32 int_value = 0;
13362306a36Sopenharmony_ci	int elem = 0;
13462306a36Sopenharmony_ci	int reqs;
13562306a36Sopenharmony_ci	int pos_values;
13662306a36Sopenharmony_ci	int ret;
13762306a36Sopenharmony_ci	int eloc;
13862306a36Sopenharmony_ci	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	for (elem = 1, eloc = 1; elem < enum_obj_count; elem++, eloc++) {
14162306a36Sopenharmony_ci		/* ONLY look at the first ENUM_ELEM_CNT elements */
14262306a36Sopenharmony_ci		if (eloc == ENUM_ELEM_CNT)
14362306a36Sopenharmony_ci			goto exit_enumeration_package;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		switch (enum_obj[elem].type) {
14662306a36Sopenharmony_ci		case ACPI_TYPE_STRING:
14762306a36Sopenharmony_ci			if (PREREQUISITES != elem && ENUM_POSSIBLE_VALUES != elem) {
14862306a36Sopenharmony_ci				ret = hp_convert_hexstr_to_str(enum_obj[elem].string.pointer,
14962306a36Sopenharmony_ci							       enum_obj[elem].string.length,
15062306a36Sopenharmony_ci							       &str_value, &value_len);
15162306a36Sopenharmony_ci				if (ret)
15262306a36Sopenharmony_ci					return -EINVAL;
15362306a36Sopenharmony_ci			}
15462306a36Sopenharmony_ci			break;
15562306a36Sopenharmony_ci		case ACPI_TYPE_INTEGER:
15662306a36Sopenharmony_ci			int_value = (u32)enum_obj[elem].integer.value;
15762306a36Sopenharmony_ci			break;
15862306a36Sopenharmony_ci		default:
15962306a36Sopenharmony_ci			pr_warn("Unsupported object type [%d]\n", enum_obj[elem].type);
16062306a36Sopenharmony_ci			continue;
16162306a36Sopenharmony_ci		}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci		/* Check that both expected and read object type match */
16462306a36Sopenharmony_ci		if (expected_enum_types[eloc] != enum_obj[elem].type) {
16562306a36Sopenharmony_ci			pr_err("Error expected type %d for elem %d, but got type %d instead\n",
16662306a36Sopenharmony_ci			       expected_enum_types[eloc], elem, enum_obj[elem].type);
16762306a36Sopenharmony_ci			kfree(str_value);
16862306a36Sopenharmony_ci			return -EIO;
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		/* Assign appropriate element value to corresponding field */
17262306a36Sopenharmony_ci		switch (eloc) {
17362306a36Sopenharmony_ci		case NAME:
17462306a36Sopenharmony_ci		case VALUE:
17562306a36Sopenharmony_ci			break;
17662306a36Sopenharmony_ci		case PATH:
17762306a36Sopenharmony_ci			strscpy(enum_data->common.path, str_value,
17862306a36Sopenharmony_ci				sizeof(enum_data->common.path));
17962306a36Sopenharmony_ci			break;
18062306a36Sopenharmony_ci		case IS_READONLY:
18162306a36Sopenharmony_ci			enum_data->common.is_readonly = int_value;
18262306a36Sopenharmony_ci			break;
18362306a36Sopenharmony_ci		case DISPLAY_IN_UI:
18462306a36Sopenharmony_ci			enum_data->common.display_in_ui = int_value;
18562306a36Sopenharmony_ci			break;
18662306a36Sopenharmony_ci		case REQUIRES_PHYSICAL_PRESENCE:
18762306a36Sopenharmony_ci			enum_data->common.requires_physical_presence = int_value;
18862306a36Sopenharmony_ci			break;
18962306a36Sopenharmony_ci		case SEQUENCE:
19062306a36Sopenharmony_ci			enum_data->common.sequence = int_value;
19162306a36Sopenharmony_ci			break;
19262306a36Sopenharmony_ci		case PREREQUISITES_SIZE:
19362306a36Sopenharmony_ci			if (int_value > MAX_PREREQUISITES_SIZE) {
19462306a36Sopenharmony_ci				pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed\n");
19562306a36Sopenharmony_ci				int_value = MAX_PREREQUISITES_SIZE;
19662306a36Sopenharmony_ci			}
19762306a36Sopenharmony_ci			enum_data->common.prerequisites_size = int_value;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci			/*
20062306a36Sopenharmony_ci			 * This step is needed to keep the expected
20162306a36Sopenharmony_ci			 * element list pointing to the right obj[elem].type
20262306a36Sopenharmony_ci			 * when the size is zero. PREREQUISITES
20362306a36Sopenharmony_ci			 * object is omitted by BIOS when the size is
20462306a36Sopenharmony_ci			 * zero.
20562306a36Sopenharmony_ci			 */
20662306a36Sopenharmony_ci			if (int_value == 0)
20762306a36Sopenharmony_ci				eloc++;
20862306a36Sopenharmony_ci			break;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci		case PREREQUISITES:
21162306a36Sopenharmony_ci			size = min_t(u32, enum_data->common.prerequisites_size, MAX_PREREQUISITES_SIZE);
21262306a36Sopenharmony_ci			for (reqs = 0; reqs < size; reqs++) {
21362306a36Sopenharmony_ci				if (elem >= enum_obj_count) {
21462306a36Sopenharmony_ci					pr_err("Error enum-objects package is too small\n");
21562306a36Sopenharmony_ci					return -EINVAL;
21662306a36Sopenharmony_ci				}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci				ret = hp_convert_hexstr_to_str(enum_obj[elem + reqs].string.pointer,
21962306a36Sopenharmony_ci							       enum_obj[elem + reqs].string.length,
22062306a36Sopenharmony_ci							       &str_value, &value_len);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci				if (ret)
22362306a36Sopenharmony_ci					return -EINVAL;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci				strscpy(enum_data->common.prerequisites[reqs],
22662306a36Sopenharmony_ci					str_value,
22762306a36Sopenharmony_ci					sizeof(enum_data->common.prerequisites[reqs]));
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci				kfree(str_value);
23062306a36Sopenharmony_ci				str_value = NULL;
23162306a36Sopenharmony_ci			}
23262306a36Sopenharmony_ci			break;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		case SECURITY_LEVEL:
23562306a36Sopenharmony_ci			enum_data->common.security_level = int_value;
23662306a36Sopenharmony_ci			break;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		case ENUM_CURRENT_VALUE:
23962306a36Sopenharmony_ci			strscpy(enum_data->current_value,
24062306a36Sopenharmony_ci				str_value, sizeof(enum_data->current_value));
24162306a36Sopenharmony_ci			break;
24262306a36Sopenharmony_ci		case ENUM_SIZE:
24362306a36Sopenharmony_ci			if (int_value > MAX_VALUES_SIZE) {
24462306a36Sopenharmony_ci				pr_warn("Possible number values size value exceeded the maximum number of elements supported or data may be malformed\n");
24562306a36Sopenharmony_ci				int_value = MAX_VALUES_SIZE;
24662306a36Sopenharmony_ci			}
24762306a36Sopenharmony_ci			enum_data->possible_values_size = int_value;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci			/*
25062306a36Sopenharmony_ci			 * This step is needed to keep the expected
25162306a36Sopenharmony_ci			 * element list pointing to the right obj[elem].type
25262306a36Sopenharmony_ci			 * when the size is zero. POSSIBLE_VALUES
25362306a36Sopenharmony_ci			 * object is omitted by BIOS when the size is zero.
25462306a36Sopenharmony_ci			 */
25562306a36Sopenharmony_ci			if (int_value == 0)
25662306a36Sopenharmony_ci				eloc++;
25762306a36Sopenharmony_ci			break;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		case ENUM_POSSIBLE_VALUES:
26062306a36Sopenharmony_ci			size = enum_data->possible_values_size;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci			for (pos_values = 0; pos_values < size && pos_values < MAX_VALUES_SIZE;
26362306a36Sopenharmony_ci			     pos_values++) {
26462306a36Sopenharmony_ci				if (elem >= enum_obj_count) {
26562306a36Sopenharmony_ci					pr_err("Error enum-objects package is too small\n");
26662306a36Sopenharmony_ci					return -EINVAL;
26762306a36Sopenharmony_ci				}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci				ret = hp_convert_hexstr_to_str(enum_obj[elem + pos_values].string.pointer,
27062306a36Sopenharmony_ci							       enum_obj[elem + pos_values].string.length,
27162306a36Sopenharmony_ci							       &str_value, &value_len);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci				if (ret)
27462306a36Sopenharmony_ci					return -EINVAL;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci				/*
27762306a36Sopenharmony_ci				 * ignore strings when possible values size
27862306a36Sopenharmony_ci				 * is greater than MAX_VALUES_SIZE
27962306a36Sopenharmony_ci				 */
28062306a36Sopenharmony_ci				if (size < MAX_VALUES_SIZE)
28162306a36Sopenharmony_ci					strscpy(enum_data->possible_values[pos_values],
28262306a36Sopenharmony_ci						str_value,
28362306a36Sopenharmony_ci						sizeof(enum_data->possible_values[pos_values]));
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci				kfree(str_value);
28662306a36Sopenharmony_ci				str_value = NULL;
28762306a36Sopenharmony_ci			}
28862306a36Sopenharmony_ci			break;
28962306a36Sopenharmony_ci		default:
29062306a36Sopenharmony_ci			pr_warn("Invalid element: %d found in Enumeration attribute or data may be malformed\n", elem);
29162306a36Sopenharmony_ci			break;
29262306a36Sopenharmony_ci		}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		kfree(str_value);
29562306a36Sopenharmony_ci		str_value = NULL;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ciexit_enumeration_package:
29962306a36Sopenharmony_ci	kfree(str_value);
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/**
30462306a36Sopenharmony_ci * hp_populate_enumeration_package_data() -
30562306a36Sopenharmony_ci * Populate all properties of an instance under enumeration attribute
30662306a36Sopenharmony_ci *
30762306a36Sopenharmony_ci * @enum_obj: ACPI object with enumeration data
30862306a36Sopenharmony_ci * @instance_id: The instance to enumerate
30962306a36Sopenharmony_ci * @attr_name_kobj: The parent kernel object
31062306a36Sopenharmony_ci */
31162306a36Sopenharmony_ciint hp_populate_enumeration_package_data(union acpi_object *enum_obj,
31262306a36Sopenharmony_ci					 int instance_id,
31362306a36Sopenharmony_ci					 struct kobject *attr_name_kobj)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	enum_data->attr_name_kobj = attr_name_kobj;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	hp_populate_enumeration_elements_from_package(enum_obj,
32062306a36Sopenharmony_ci						      enum_obj->package.count,
32162306a36Sopenharmony_ci						      instance_id);
32262306a36Sopenharmony_ci	hp_update_attribute_permissions(enum_data->common.is_readonly,
32362306a36Sopenharmony_ci					&enumeration_current_val);
32462306a36Sopenharmony_ci	/*
32562306a36Sopenharmony_ci	 * Several attributes have names such "MONDAY". Friendly
32662306a36Sopenharmony_ci	 * user nane is generated to make the name more descriptive
32762306a36Sopenharmony_ci	 */
32862306a36Sopenharmony_ci	hp_friendly_user_name_update(enum_data->common.path,
32962306a36Sopenharmony_ci				     attr_name_kobj->name,
33062306a36Sopenharmony_ci				     enum_data->common.display_name,
33162306a36Sopenharmony_ci				     sizeof(enum_data->common.display_name));
33262306a36Sopenharmony_ci	return sysfs_create_group(attr_name_kobj, &enumeration_attr_group);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int hp_populate_enumeration_elements_from_buffer(u8 *buffer_ptr, u32 *buffer_size,
33662306a36Sopenharmony_ci							int instance_id)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	int values;
33962306a36Sopenharmony_ci	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
34062306a36Sopenharmony_ci	int ret = 0;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/*
34362306a36Sopenharmony_ci	 * Only data relevant to this driver and its functionality is
34462306a36Sopenharmony_ci	 * read. BIOS defines the order in which each * element is
34562306a36Sopenharmony_ci	 * read. Element 0 data is not relevant to this
34662306a36Sopenharmony_ci	 * driver hence it is ignored. For clarity, all element names
34762306a36Sopenharmony_ci	 * (DISPLAY_IN_UI) which defines the order in which is read
34862306a36Sopenharmony_ci	 * and the name matches the variable where the data is stored.
34962306a36Sopenharmony_ci	 *
35062306a36Sopenharmony_ci	 * In earlier implementation, reported errors were ignored
35162306a36Sopenharmony_ci	 * causing the data to remain uninitialized. It is not
35262306a36Sopenharmony_ci	 * possible to determine if data read from BIOS is valid or
35362306a36Sopenharmony_ci	 * not. It is for this reason functions may return a error
35462306a36Sopenharmony_ci	 * without validating the data itself.
35562306a36Sopenharmony_ci	 */
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	// VALUE:
35862306a36Sopenharmony_ci	ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size, enum_data->current_value,
35962306a36Sopenharmony_ci					sizeof(enum_data->current_value));
36062306a36Sopenharmony_ci	if (ret < 0)
36162306a36Sopenharmony_ci		goto buffer_exit;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	// COMMON:
36462306a36Sopenharmony_ci	ret = hp_get_common_data_from_buffer(&buffer_ptr, buffer_size, &enum_data->common);
36562306a36Sopenharmony_ci	if (ret < 0)
36662306a36Sopenharmony_ci		goto buffer_exit;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	// ENUM_CURRENT_VALUE:
36962306a36Sopenharmony_ci	ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size,
37062306a36Sopenharmony_ci					enum_data->current_value,
37162306a36Sopenharmony_ci					sizeof(enum_data->current_value));
37262306a36Sopenharmony_ci	if (ret < 0)
37362306a36Sopenharmony_ci		goto buffer_exit;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	// ENUM_SIZE:
37662306a36Sopenharmony_ci	ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size,
37762306a36Sopenharmony_ci					 &enum_data->possible_values_size);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (enum_data->possible_values_size > MAX_VALUES_SIZE) {
38062306a36Sopenharmony_ci		/* Report a message and limit possible values size to maximum value */
38162306a36Sopenharmony_ci		pr_warn("Enum Possible size value exceeded the maximum number of elements supported or data may be malformed\n");
38262306a36Sopenharmony_ci		enum_data->possible_values_size = MAX_VALUES_SIZE;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	// ENUM_POSSIBLE_VALUES:
38662306a36Sopenharmony_ci	for (values = 0; values < enum_data->possible_values_size; values++) {
38762306a36Sopenharmony_ci		ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size,
38862306a36Sopenharmony_ci						enum_data->possible_values[values],
38962306a36Sopenharmony_ci						sizeof(enum_data->possible_values[values]));
39062306a36Sopenharmony_ci		if (ret < 0)
39162306a36Sopenharmony_ci			break;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cibuffer_exit:
39562306a36Sopenharmony_ci	return ret;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci/**
39962306a36Sopenharmony_ci * hp_populate_enumeration_buffer_data() -
40062306a36Sopenharmony_ci * Populate all properties of an instance under enumeration attribute
40162306a36Sopenharmony_ci *
40262306a36Sopenharmony_ci * @buffer_ptr: Buffer pointer
40362306a36Sopenharmony_ci * @buffer_size: Buffer size
40462306a36Sopenharmony_ci * @instance_id: The instance to enumerate
40562306a36Sopenharmony_ci * @attr_name_kobj: The parent kernel object
40662306a36Sopenharmony_ci */
40762306a36Sopenharmony_ciint hp_populate_enumeration_buffer_data(u8 *buffer_ptr, u32 *buffer_size,
40862306a36Sopenharmony_ci					int instance_id,
40962306a36Sopenharmony_ci					struct kobject *attr_name_kobj)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
41262306a36Sopenharmony_ci	int ret = 0;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	enum_data->attr_name_kobj = attr_name_kobj;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	/* Populate enumeration elements */
41762306a36Sopenharmony_ci	ret = hp_populate_enumeration_elements_from_buffer(buffer_ptr, buffer_size,
41862306a36Sopenharmony_ci							   instance_id);
41962306a36Sopenharmony_ci	if (ret < 0)
42062306a36Sopenharmony_ci		return ret;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	hp_update_attribute_permissions(enum_data->common.is_readonly,
42362306a36Sopenharmony_ci					&enumeration_current_val);
42462306a36Sopenharmony_ci	/*
42562306a36Sopenharmony_ci	 * Several attributes have names such "MONDAY". A Friendlier
42662306a36Sopenharmony_ci	 * user nane is generated to make the name more descriptive
42762306a36Sopenharmony_ci	 */
42862306a36Sopenharmony_ci	hp_friendly_user_name_update(enum_data->common.path,
42962306a36Sopenharmony_ci				     attr_name_kobj->name,
43062306a36Sopenharmony_ci				     enum_data->common.display_name,
43162306a36Sopenharmony_ci				     sizeof(enum_data->common.display_name));
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	return sysfs_create_group(attr_name_kobj, &enumeration_attr_group);
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci/**
43762306a36Sopenharmony_ci * hp_exit_enumeration_attributes() - Clear all attribute data
43862306a36Sopenharmony_ci *
43962306a36Sopenharmony_ci * Clears all data allocated for this group of attributes
44062306a36Sopenharmony_ci */
44162306a36Sopenharmony_civoid hp_exit_enumeration_attributes(void)
44262306a36Sopenharmony_ci{
44362306a36Sopenharmony_ci	int instance_id;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	for (instance_id = 0; instance_id < bioscfg_drv.enumeration_instances_count;
44662306a36Sopenharmony_ci	     instance_id++) {
44762306a36Sopenharmony_ci		struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
44862306a36Sopenharmony_ci		struct kobject *attr_name_kobj = enum_data->attr_name_kobj;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		if (attr_name_kobj)
45162306a36Sopenharmony_ci			sysfs_remove_group(attr_name_kobj, &enumeration_attr_group);
45262306a36Sopenharmony_ci	}
45362306a36Sopenharmony_ci	bioscfg_drv.enumeration_instances_count = 0;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	kfree(bioscfg_drv.enumeration_data);
45662306a36Sopenharmony_ci	bioscfg_drv.enumeration_data = NULL;
45762306a36Sopenharmony_ci}
458