16b2770bfSJorge Lopez // SPDX-License-Identifier: GPL-2.0
26b2770bfSJorge Lopez /*
36b2770bfSJorge Lopez  * Functions corresponding to enumeration type attributes under
46b2770bfSJorge Lopez  * BIOS Enumeration GUID for use with hp-bioscfg driver.
56b2770bfSJorge Lopez  *
66b2770bfSJorge Lopez  * Copyright (c) 2022 HP Development Company, L.P.
76b2770bfSJorge Lopez  */
86b2770bfSJorge Lopez 
96b2770bfSJorge Lopez #include "bioscfg.h"
106b2770bfSJorge Lopez 
116b2770bfSJorge Lopez GET_INSTANCE_ID(enumeration);
126b2770bfSJorge Lopez 
current_value_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)136b2770bfSJorge Lopez static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
146b2770bfSJorge Lopez {
156b2770bfSJorge Lopez 	int instance_id = get_enumeration_instance_id(kobj);
166b2770bfSJorge Lopez 
176b2770bfSJorge Lopez 	if (instance_id < 0)
186b2770bfSJorge Lopez 		return -EIO;
196b2770bfSJorge Lopez 
206b2770bfSJorge Lopez 	return sysfs_emit(buf, "%s\n",
216b2770bfSJorge Lopez 			 bioscfg_drv.enumeration_data[instance_id].current_value);
226b2770bfSJorge Lopez }
236b2770bfSJorge Lopez 
246b2770bfSJorge Lopez /**
256b2770bfSJorge Lopez  * validate_enumeration_input() -
266b2770bfSJorge Lopez  * Validate input of current_value against possible values
276b2770bfSJorge Lopez  *
286b2770bfSJorge Lopez  * @instance_id: The instance on which input is validated
296b2770bfSJorge Lopez  * @buf: Input value
306b2770bfSJorge Lopez  */
validate_enumeration_input(int instance_id,const char * buf)316b2770bfSJorge Lopez static int validate_enumeration_input(int instance_id, const char *buf)
326b2770bfSJorge Lopez {
336b2770bfSJorge Lopez 	int i;
346b2770bfSJorge Lopez 	int found = 0;
356b2770bfSJorge Lopez 	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
366b2770bfSJorge Lopez 
376b2770bfSJorge Lopez 	/* Is it a read only attribute */
386b2770bfSJorge Lopez 	if (enum_data->common.is_readonly)
396b2770bfSJorge Lopez 		return -EIO;
406b2770bfSJorge Lopez 
416b2770bfSJorge Lopez 	for (i = 0; i < enum_data->possible_values_size && !found; i++)
426b2770bfSJorge Lopez 		if (!strcmp(enum_data->possible_values[i], buf))
436b2770bfSJorge Lopez 			found = 1;
446b2770bfSJorge Lopez 
456b2770bfSJorge Lopez 	if (!found)
466b2770bfSJorge Lopez 		return -EINVAL;
476b2770bfSJorge Lopez 
486b2770bfSJorge Lopez 	return 0;
496b2770bfSJorge Lopez }
506b2770bfSJorge Lopez 
update_enumeration_value(int instance_id,char * attr_value)516b2770bfSJorge Lopez static void update_enumeration_value(int instance_id, char *attr_value)
526b2770bfSJorge Lopez {
536b2770bfSJorge Lopez 	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
546b2770bfSJorge Lopez 
556b2770bfSJorge Lopez 	strscpy(enum_data->current_value,
566b2770bfSJorge Lopez 		attr_value,
576b2770bfSJorge Lopez 		sizeof(enum_data->current_value));
586b2770bfSJorge Lopez }
596b2770bfSJorge Lopez 
606b2770bfSJorge Lopez ATTRIBUTE_S_COMMON_PROPERTY_SHOW(display_name, enumeration);
616b2770bfSJorge Lopez static struct kobj_attribute enumeration_display_name =
626b2770bfSJorge Lopez 		__ATTR_RO(display_name);
636b2770bfSJorge Lopez 
646b2770bfSJorge Lopez ATTRIBUTE_PROPERTY_STORE(current_value, enumeration);
656b2770bfSJorge Lopez static struct kobj_attribute enumeration_current_val =
666b2770bfSJorge Lopez 		__ATTR_RW(current_value);
676b2770bfSJorge Lopez 
686b2770bfSJorge Lopez ATTRIBUTE_VALUES_PROPERTY_SHOW(possible_values, enumeration, SEMICOLON_SEP);
696b2770bfSJorge Lopez static struct kobj_attribute enumeration_poss_val =
706b2770bfSJorge Lopez 		__ATTR_RO(possible_values);
716b2770bfSJorge Lopez 
type_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)726b2770bfSJorge Lopez static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
736b2770bfSJorge Lopez 			 char *buf)
746b2770bfSJorge Lopez {
756b2770bfSJorge Lopez 	return sysfs_emit(buf, "enumeration\n");
766b2770bfSJorge Lopez }
776b2770bfSJorge Lopez 
786b2770bfSJorge Lopez static struct kobj_attribute enumeration_type =
796b2770bfSJorge Lopez 		__ATTR_RO(type);
806b2770bfSJorge Lopez 
816b2770bfSJorge Lopez static struct attribute *enumeration_attrs[] = {
826b2770bfSJorge Lopez 	&common_display_langcode.attr,
836b2770bfSJorge Lopez 	&enumeration_display_name.attr,
846b2770bfSJorge Lopez 	&enumeration_current_val.attr,
856b2770bfSJorge Lopez 	&enumeration_poss_val.attr,
866b2770bfSJorge Lopez 	&enumeration_type.attr,
876b2770bfSJorge Lopez 	NULL
886b2770bfSJorge Lopez };
896b2770bfSJorge Lopez 
906b2770bfSJorge Lopez static const struct attribute_group enumeration_attr_group = {
916b2770bfSJorge Lopez 	.attrs = enumeration_attrs,
926b2770bfSJorge Lopez };
936b2770bfSJorge Lopez 
hp_alloc_enumeration_data(void)946b2770bfSJorge Lopez int hp_alloc_enumeration_data(void)
956b2770bfSJorge Lopez {
966b2770bfSJorge Lopez 	bioscfg_drv.enumeration_instances_count =
976b2770bfSJorge Lopez 		hp_get_instance_count(HP_WMI_BIOS_ENUMERATION_GUID);
986b2770bfSJorge Lopez 
996b2770bfSJorge Lopez 	bioscfg_drv.enumeration_data = kcalloc(bioscfg_drv.enumeration_instances_count,
1006b2770bfSJorge Lopez 					       sizeof(*bioscfg_drv.enumeration_data), GFP_KERNEL);
1016b2770bfSJorge Lopez 	if (!bioscfg_drv.enumeration_data) {
1026b2770bfSJorge Lopez 		bioscfg_drv.enumeration_instances_count = 0;
1036b2770bfSJorge Lopez 		return -ENOMEM;
1046b2770bfSJorge Lopez 	}
1056b2770bfSJorge Lopez 	return 0;
1066b2770bfSJorge Lopez }
1076b2770bfSJorge Lopez 
1086b2770bfSJorge Lopez /* Expected Values types associated with each element */
1096b2770bfSJorge Lopez static const acpi_object_type expected_enum_types[] = {
1106b2770bfSJorge Lopez 	[NAME] = ACPI_TYPE_STRING,
1116b2770bfSJorge Lopez 	[VALUE] = ACPI_TYPE_STRING,
1126b2770bfSJorge Lopez 	[PATH] = ACPI_TYPE_STRING,
1136b2770bfSJorge Lopez 	[IS_READONLY] = ACPI_TYPE_INTEGER,
1146b2770bfSJorge Lopez 	[DISPLAY_IN_UI] = ACPI_TYPE_INTEGER,
1156b2770bfSJorge Lopez 	[REQUIRES_PHYSICAL_PRESENCE] = ACPI_TYPE_INTEGER,
1166b2770bfSJorge Lopez 	[SEQUENCE] = ACPI_TYPE_INTEGER,
1176b2770bfSJorge Lopez 	[PREREQUISITES_SIZE] = ACPI_TYPE_INTEGER,
1186b2770bfSJorge Lopez 	[PREREQUISITES] = ACPI_TYPE_STRING,
1196b2770bfSJorge Lopez 	[SECURITY_LEVEL] = ACPI_TYPE_INTEGER,
1206b2770bfSJorge Lopez 	[ENUM_CURRENT_VALUE] = ACPI_TYPE_STRING,
1216b2770bfSJorge Lopez 	[ENUM_SIZE] = ACPI_TYPE_INTEGER,
1226b2770bfSJorge Lopez 	[ENUM_POSSIBLE_VALUES] = ACPI_TYPE_STRING,
1236b2770bfSJorge Lopez };
1246b2770bfSJorge Lopez 
hp_populate_enumeration_elements_from_package(union acpi_object * enum_obj,int enum_obj_count,int instance_id)1256b2770bfSJorge Lopez static int hp_populate_enumeration_elements_from_package(union acpi_object *enum_obj,
1266b2770bfSJorge Lopez 							 int enum_obj_count,
1276b2770bfSJorge Lopez 							 int instance_id)
1286b2770bfSJorge Lopez {
1296b2770bfSJorge Lopez 	char *str_value = NULL;
1306b2770bfSJorge Lopez 	int value_len;
1316b2770bfSJorge Lopez 	u32 size = 0;
13280d7ba30SJorge Lopez 	u32 int_value = 0;
1336b2770bfSJorge Lopez 	int elem = 0;
1346b2770bfSJorge Lopez 	int reqs;
1356b2770bfSJorge Lopez 	int pos_values;
1366b2770bfSJorge Lopez 	int ret;
1376b2770bfSJorge Lopez 	int eloc;
1386b2770bfSJorge Lopez 	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
1396b2770bfSJorge Lopez 
1406b2770bfSJorge Lopez 	for (elem = 1, eloc = 1; elem < enum_obj_count; elem++, eloc++) {
1416b2770bfSJorge Lopez 		/* ONLY look at the first ENUM_ELEM_CNT elements */
1426b2770bfSJorge Lopez 		if (eloc == ENUM_ELEM_CNT)
1436b2770bfSJorge Lopez 			goto exit_enumeration_package;
1446b2770bfSJorge Lopez 
1456b2770bfSJorge Lopez 		switch (enum_obj[elem].type) {
1466b2770bfSJorge Lopez 		case ACPI_TYPE_STRING:
1476b2770bfSJorge Lopez 			if (PREREQUISITES != elem && ENUM_POSSIBLE_VALUES != elem) {
1486b2770bfSJorge Lopez 				ret = hp_convert_hexstr_to_str(enum_obj[elem].string.pointer,
1496b2770bfSJorge Lopez 							       enum_obj[elem].string.length,
1506b2770bfSJorge Lopez 							       &str_value, &value_len);
1516b2770bfSJorge Lopez 				if (ret)
1526b2770bfSJorge Lopez 					return -EINVAL;
1536b2770bfSJorge Lopez 			}
1546b2770bfSJorge Lopez 			break;
1556b2770bfSJorge Lopez 		case ACPI_TYPE_INTEGER:
1566b2770bfSJorge Lopez 			int_value = (u32)enum_obj[elem].integer.value;
1576b2770bfSJorge Lopez 			break;
1586b2770bfSJorge Lopez 		default:
1596b2770bfSJorge Lopez 			pr_warn("Unsupported object type [%d]\n", enum_obj[elem].type);
1606b2770bfSJorge Lopez 			continue;
1616b2770bfSJorge Lopez 		}
1626b2770bfSJorge Lopez 
1636b2770bfSJorge Lopez 		/* Check that both expected and read object type match */
1646b2770bfSJorge Lopez 		if (expected_enum_types[eloc] != enum_obj[elem].type) {
1656b2770bfSJorge Lopez 			pr_err("Error expected type %d for elem %d, but got type %d instead\n",
1666b2770bfSJorge Lopez 			       expected_enum_types[eloc], elem, enum_obj[elem].type);
167467d4163SJorge Lopez 			kfree(str_value);
1686b2770bfSJorge Lopez 			return -EIO;
1696b2770bfSJorge Lopez 		}
1706b2770bfSJorge Lopez 
1716b2770bfSJorge Lopez 		/* Assign appropriate element value to corresponding field */
1726b2770bfSJorge Lopez 		switch (eloc) {
1736b2770bfSJorge Lopez 		case NAME:
1746b2770bfSJorge Lopez 		case VALUE:
1756b2770bfSJorge Lopez 			break;
1766b2770bfSJorge Lopez 		case PATH:
1776b2770bfSJorge Lopez 			strscpy(enum_data->common.path, str_value,
1786b2770bfSJorge Lopez 				sizeof(enum_data->common.path));
1796b2770bfSJorge Lopez 			break;
1806b2770bfSJorge Lopez 		case IS_READONLY:
1816b2770bfSJorge Lopez 			enum_data->common.is_readonly = int_value;
1826b2770bfSJorge Lopez 			break;
1836b2770bfSJorge Lopez 		case DISPLAY_IN_UI:
1846b2770bfSJorge Lopez 			enum_data->common.display_in_ui = int_value;
1856b2770bfSJorge Lopez 			break;
1866b2770bfSJorge Lopez 		case REQUIRES_PHYSICAL_PRESENCE:
1876b2770bfSJorge Lopez 			enum_data->common.requires_physical_presence = int_value;
1886b2770bfSJorge Lopez 			break;
1896b2770bfSJorge Lopez 		case SEQUENCE:
1906b2770bfSJorge Lopez 			enum_data->common.sequence = int_value;
1916b2770bfSJorge Lopez 			break;
1926b2770bfSJorge Lopez 		case PREREQUISITES_SIZE:
19308f1f212SJorge Lopez 			if (int_value > MAX_PREREQUISITES_SIZE) {
1946b2770bfSJorge Lopez 				pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed\n");
19508f1f212SJorge Lopez 				int_value = MAX_PREREQUISITES_SIZE;
19608f1f212SJorge Lopez 			}
19708f1f212SJorge Lopez 			enum_data->common.prerequisites_size = int_value;
1986b2770bfSJorge Lopez 
1996b2770bfSJorge Lopez 			/*
200a585400bSJorge Lopez 			 * This step is needed to keep the expected
2016b2770bfSJorge Lopez 			 * element list pointing to the right obj[elem].type
2026b2770bfSJorge Lopez 			 * when the size is zero. PREREQUISITES
2036b2770bfSJorge Lopez 			 * object is omitted by BIOS when the size is
2046b2770bfSJorge Lopez 			 * zero.
2056b2770bfSJorge Lopez 			 */
2066b2770bfSJorge Lopez 			if (int_value == 0)
2076b2770bfSJorge Lopez 				eloc++;
2086b2770bfSJorge Lopez 			break;
2096b2770bfSJorge Lopez 
2106b2770bfSJorge Lopez 		case PREREQUISITES:
2116b2770bfSJorge Lopez 			size = min_t(u32, enum_data->common.prerequisites_size, MAX_PREREQUISITES_SIZE);
2126b2770bfSJorge Lopez 			for (reqs = 0; reqs < size; reqs++) {
2136b2770bfSJorge Lopez 				if (elem >= enum_obj_count) {
2146b2770bfSJorge Lopez 					pr_err("Error enum-objects package is too small\n");
2156b2770bfSJorge Lopez 					return -EINVAL;
2166b2770bfSJorge Lopez 				}
2176b2770bfSJorge Lopez 
2186b2770bfSJorge Lopez 				ret = hp_convert_hexstr_to_str(enum_obj[elem + reqs].string.pointer,
2196b2770bfSJorge Lopez 							       enum_obj[elem + reqs].string.length,
2206b2770bfSJorge Lopez 							       &str_value, &value_len);
2216b2770bfSJorge Lopez 
2226b2770bfSJorge Lopez 				if (ret)
2236b2770bfSJorge Lopez 					return -EINVAL;
2246b2770bfSJorge Lopez 
2256b2770bfSJorge Lopez 				strscpy(enum_data->common.prerequisites[reqs],
2266b2770bfSJorge Lopez 					str_value,
2276b2770bfSJorge Lopez 					sizeof(enum_data->common.prerequisites[reqs]));
2286b2770bfSJorge Lopez 
2296b2770bfSJorge Lopez 				kfree(str_value);
230467d4163SJorge Lopez 				str_value = NULL;
2316b2770bfSJorge Lopez 			}
2326b2770bfSJorge Lopez 			break;
2336b2770bfSJorge Lopez 
2346b2770bfSJorge Lopez 		case SECURITY_LEVEL:
2356b2770bfSJorge Lopez 			enum_data->common.security_level = int_value;
2366b2770bfSJorge Lopez 			break;
2376b2770bfSJorge Lopez 
2386b2770bfSJorge Lopez 		case ENUM_CURRENT_VALUE:
2396b2770bfSJorge Lopez 			strscpy(enum_data->current_value,
2406b2770bfSJorge Lopez 				str_value, sizeof(enum_data->current_value));
2416b2770bfSJorge Lopez 			break;
2426b2770bfSJorge Lopez 		case ENUM_SIZE:
243*42efc9e6SJorge Lopez 			if (int_value > MAX_VALUES_SIZE) {
2446b2770bfSJorge Lopez 				pr_warn("Possible number values size value exceeded the maximum number of elements supported or data may be malformed\n");
245*42efc9e6SJorge Lopez 				int_value = MAX_VALUES_SIZE;
246*42efc9e6SJorge Lopez 			}
247*42efc9e6SJorge Lopez 			enum_data->possible_values_size = int_value;
2486b2770bfSJorge Lopez 
2496b2770bfSJorge Lopez 			/*
250a585400bSJorge Lopez 			 * This step is needed to keep the expected
2516b2770bfSJorge Lopez 			 * element list pointing to the right obj[elem].type
2526b2770bfSJorge Lopez 			 * when the size is zero. POSSIBLE_VALUES
2536b2770bfSJorge Lopez 			 * object is omitted by BIOS when the size is zero.
2546b2770bfSJorge Lopez 			 */
2556b2770bfSJorge Lopez 			if (int_value == 0)
2566b2770bfSJorge Lopez 				eloc++;
2576b2770bfSJorge Lopez 			break;
2586b2770bfSJorge Lopez 
2596b2770bfSJorge Lopez 		case ENUM_POSSIBLE_VALUES:
2606b2770bfSJorge Lopez 			size = enum_data->possible_values_size;
2616b2770bfSJorge Lopez 
2626b2770bfSJorge Lopez 			for (pos_values = 0; pos_values < size && pos_values < MAX_VALUES_SIZE;
2636b2770bfSJorge Lopez 			     pos_values++) {
2646b2770bfSJorge Lopez 				if (elem >= enum_obj_count) {
2656b2770bfSJorge Lopez 					pr_err("Error enum-objects package is too small\n");
2666b2770bfSJorge Lopez 					return -EINVAL;
2676b2770bfSJorge Lopez 				}
2686b2770bfSJorge Lopez 
2696b2770bfSJorge Lopez 				ret = hp_convert_hexstr_to_str(enum_obj[elem + pos_values].string.pointer,
2706b2770bfSJorge Lopez 							       enum_obj[elem + pos_values].string.length,
2716b2770bfSJorge Lopez 							       &str_value, &value_len);
2726b2770bfSJorge Lopez 
2736b2770bfSJorge Lopez 				if (ret)
2746b2770bfSJorge Lopez 					return -EINVAL;
2756b2770bfSJorge Lopez 
2766b2770bfSJorge Lopez 				/*
2776b2770bfSJorge Lopez 				 * ignore strings when possible values size
2786b2770bfSJorge Lopez 				 * is greater than MAX_VALUES_SIZE
2796b2770bfSJorge Lopez 				 */
2806b2770bfSJorge Lopez 				if (size < MAX_VALUES_SIZE)
2816b2770bfSJorge Lopez 					strscpy(enum_data->possible_values[pos_values],
2826b2770bfSJorge Lopez 						str_value,
2836b2770bfSJorge Lopez 						sizeof(enum_data->possible_values[pos_values]));
284467d4163SJorge Lopez 
285467d4163SJorge Lopez 				kfree(str_value);
286467d4163SJorge Lopez 				str_value = NULL;
2876b2770bfSJorge Lopez 			}
2886b2770bfSJorge Lopez 			break;
2896b2770bfSJorge Lopez 		default:
2906b2770bfSJorge Lopez 			pr_warn("Invalid element: %d found in Enumeration attribute or data may be malformed\n", elem);
2916b2770bfSJorge Lopez 			break;
2926b2770bfSJorge Lopez 		}
2936b2770bfSJorge Lopez 
2946b2770bfSJorge Lopez 		kfree(str_value);
295467d4163SJorge Lopez 		str_value = NULL;
2966b2770bfSJorge Lopez 	}
2976b2770bfSJorge Lopez 
2986b2770bfSJorge Lopez exit_enumeration_package:
2996b2770bfSJorge Lopez 	kfree(str_value);
3006b2770bfSJorge Lopez 	return 0;
3016b2770bfSJorge Lopez }
3026b2770bfSJorge Lopez 
3036b2770bfSJorge Lopez /**
3046b2770bfSJorge Lopez  * hp_populate_enumeration_package_data() -
3056b2770bfSJorge Lopez  * Populate all properties of an instance under enumeration attribute
3066b2770bfSJorge Lopez  *
3076b2770bfSJorge Lopez  * @enum_obj: ACPI object with enumeration data
3086b2770bfSJorge Lopez  * @instance_id: The instance to enumerate
3096b2770bfSJorge Lopez  * @attr_name_kobj: The parent kernel object
3106b2770bfSJorge Lopez  */
hp_populate_enumeration_package_data(union acpi_object * enum_obj,int instance_id,struct kobject * attr_name_kobj)3116b2770bfSJorge Lopez int hp_populate_enumeration_package_data(union acpi_object *enum_obj,
3126b2770bfSJorge Lopez 					 int instance_id,
3136b2770bfSJorge Lopez 					 struct kobject *attr_name_kobj)
3146b2770bfSJorge Lopez {
3156b2770bfSJorge Lopez 	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
3166b2770bfSJorge Lopez 
3176b2770bfSJorge Lopez 	enum_data->attr_name_kobj = attr_name_kobj;
3186b2770bfSJorge Lopez 
3196b2770bfSJorge Lopez 	hp_populate_enumeration_elements_from_package(enum_obj,
3206b2770bfSJorge Lopez 						      enum_obj->package.count,
3216b2770bfSJorge Lopez 						      instance_id);
3226b2770bfSJorge Lopez 	hp_update_attribute_permissions(enum_data->common.is_readonly,
3236b2770bfSJorge Lopez 					&enumeration_current_val);
3246b2770bfSJorge Lopez 	/*
3256b2770bfSJorge Lopez 	 * Several attributes have names such "MONDAY". Friendly
3266b2770bfSJorge Lopez 	 * user nane is generated to make the name more descriptive
3276b2770bfSJorge Lopez 	 */
3286b2770bfSJorge Lopez 	hp_friendly_user_name_update(enum_data->common.path,
3296b2770bfSJorge Lopez 				     attr_name_kobj->name,
3306b2770bfSJorge Lopez 				     enum_data->common.display_name,
3316b2770bfSJorge Lopez 				     sizeof(enum_data->common.display_name));
3326b2770bfSJorge Lopez 	return sysfs_create_group(attr_name_kobj, &enumeration_attr_group);
3336b2770bfSJorge Lopez }
3346b2770bfSJorge Lopez 
hp_populate_enumeration_elements_from_buffer(u8 * buffer_ptr,u32 * buffer_size,int instance_id)3356b2770bfSJorge Lopez static int hp_populate_enumeration_elements_from_buffer(u8 *buffer_ptr, u32 *buffer_size,
3366b2770bfSJorge Lopez 							int instance_id)
3376b2770bfSJorge Lopez {
3386b2770bfSJorge Lopez 	int values;
3396b2770bfSJorge Lopez 	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
3406b2770bfSJorge Lopez 	int ret = 0;
3416b2770bfSJorge Lopez 
3426b2770bfSJorge Lopez 	/*
3436b2770bfSJorge Lopez 	 * Only data relevant to this driver and its functionality is
3446b2770bfSJorge Lopez 	 * read. BIOS defines the order in which each * element is
3456b2770bfSJorge Lopez 	 * read. Element 0 data is not relevant to this
3466b2770bfSJorge Lopez 	 * driver hence it is ignored. For clarity, all element names
3476b2770bfSJorge Lopez 	 * (DISPLAY_IN_UI) which defines the order in which is read
3486b2770bfSJorge Lopez 	 * and the name matches the variable where the data is stored.
3496b2770bfSJorge Lopez 	 *
3506b2770bfSJorge Lopez 	 * In earlier implementation, reported errors were ignored
3516b2770bfSJorge Lopez 	 * causing the data to remain uninitialized. It is not
3526b2770bfSJorge Lopez 	 * possible to determine if data read from BIOS is valid or
3536b2770bfSJorge Lopez 	 * not. It is for this reason functions may return a error
3546b2770bfSJorge Lopez 	 * without validating the data itself.
3556b2770bfSJorge Lopez 	 */
3566b2770bfSJorge Lopez 
3576b2770bfSJorge Lopez 	// VALUE:
3586b2770bfSJorge Lopez 	ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size, enum_data->current_value,
3596b2770bfSJorge Lopez 					sizeof(enum_data->current_value));
3606b2770bfSJorge Lopez 	if (ret < 0)
3616b2770bfSJorge Lopez 		goto buffer_exit;
3626b2770bfSJorge Lopez 
3636b2770bfSJorge Lopez 	// COMMON:
3646b2770bfSJorge Lopez 	ret = hp_get_common_data_from_buffer(&buffer_ptr, buffer_size, &enum_data->common);
3656b2770bfSJorge Lopez 	if (ret < 0)
3666b2770bfSJorge Lopez 		goto buffer_exit;
3676b2770bfSJorge Lopez 
3686b2770bfSJorge Lopez 	// ENUM_CURRENT_VALUE:
3696b2770bfSJorge Lopez 	ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size,
3706b2770bfSJorge Lopez 					enum_data->current_value,
3716b2770bfSJorge Lopez 					sizeof(enum_data->current_value));
3726b2770bfSJorge Lopez 	if (ret < 0)
3736b2770bfSJorge Lopez 		goto buffer_exit;
3746b2770bfSJorge Lopez 
3756b2770bfSJorge Lopez 	// ENUM_SIZE:
3766b2770bfSJorge Lopez 	ret = hp_get_integer_from_buffer(&buffer_ptr, buffer_size,
3776b2770bfSJorge Lopez 					 &enum_data->possible_values_size);
3786b2770bfSJorge Lopez 
3796b2770bfSJorge Lopez 	if (enum_data->possible_values_size > MAX_VALUES_SIZE) {
3806b2770bfSJorge Lopez 		/* Report a message and limit possible values size to maximum value */
3816b2770bfSJorge Lopez 		pr_warn("Enum Possible size value exceeded the maximum number of elements supported or data may be malformed\n");
3826b2770bfSJorge Lopez 		enum_data->possible_values_size = MAX_VALUES_SIZE;
3836b2770bfSJorge Lopez 	}
3846b2770bfSJorge Lopez 
3856b2770bfSJorge Lopez 	// ENUM_POSSIBLE_VALUES:
3866b2770bfSJorge Lopez 	for (values = 0; values < enum_data->possible_values_size; values++) {
3876b2770bfSJorge Lopez 		ret = hp_get_string_from_buffer(&buffer_ptr, buffer_size,
3886b2770bfSJorge Lopez 						enum_data->possible_values[values],
3896b2770bfSJorge Lopez 						sizeof(enum_data->possible_values[values]));
3906b2770bfSJorge Lopez 		if (ret < 0)
3916b2770bfSJorge Lopez 			break;
3926b2770bfSJorge Lopez 	}
3936b2770bfSJorge Lopez 
3946b2770bfSJorge Lopez buffer_exit:
3956b2770bfSJorge Lopez 	return ret;
3966b2770bfSJorge Lopez }
3976b2770bfSJorge Lopez 
3986b2770bfSJorge Lopez /**
3996b2770bfSJorge Lopez  * hp_populate_enumeration_buffer_data() -
4006b2770bfSJorge Lopez  * Populate all properties of an instance under enumeration attribute
4016b2770bfSJorge Lopez  *
4026b2770bfSJorge Lopez  * @buffer_ptr: Buffer pointer
4036b2770bfSJorge Lopez  * @buffer_size: Buffer size
4046b2770bfSJorge Lopez  * @instance_id: The instance to enumerate
4056b2770bfSJorge Lopez  * @attr_name_kobj: The parent kernel object
4066b2770bfSJorge Lopez  */
hp_populate_enumeration_buffer_data(u8 * buffer_ptr,u32 * buffer_size,int instance_id,struct kobject * attr_name_kobj)4076b2770bfSJorge Lopez int hp_populate_enumeration_buffer_data(u8 *buffer_ptr, u32 *buffer_size,
4086b2770bfSJorge Lopez 					int instance_id,
4096b2770bfSJorge Lopez 					struct kobject *attr_name_kobj)
4106b2770bfSJorge Lopez {
4116b2770bfSJorge Lopez 	struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
4126b2770bfSJorge Lopez 	int ret = 0;
4136b2770bfSJorge Lopez 
4146b2770bfSJorge Lopez 	enum_data->attr_name_kobj = attr_name_kobj;
4156b2770bfSJorge Lopez 
4166b2770bfSJorge Lopez 	/* Populate enumeration elements */
4176b2770bfSJorge Lopez 	ret = hp_populate_enumeration_elements_from_buffer(buffer_ptr, buffer_size,
4186b2770bfSJorge Lopez 							   instance_id);
4196b2770bfSJorge Lopez 	if (ret < 0)
4206b2770bfSJorge Lopez 		return ret;
4216b2770bfSJorge Lopez 
4226b2770bfSJorge Lopez 	hp_update_attribute_permissions(enum_data->common.is_readonly,
4236b2770bfSJorge Lopez 					&enumeration_current_val);
4246b2770bfSJorge Lopez 	/*
4256b2770bfSJorge Lopez 	 * Several attributes have names such "MONDAY". A Friendlier
4266b2770bfSJorge Lopez 	 * user nane is generated to make the name more descriptive
4276b2770bfSJorge Lopez 	 */
4286b2770bfSJorge Lopez 	hp_friendly_user_name_update(enum_data->common.path,
4296b2770bfSJorge Lopez 				     attr_name_kobj->name,
4306b2770bfSJorge Lopez 				     enum_data->common.display_name,
4316b2770bfSJorge Lopez 				     sizeof(enum_data->common.display_name));
4326b2770bfSJorge Lopez 
4336b2770bfSJorge Lopez 	return sysfs_create_group(attr_name_kobj, &enumeration_attr_group);
4346b2770bfSJorge Lopez }
4356b2770bfSJorge Lopez 
4366b2770bfSJorge Lopez /**
4376b2770bfSJorge Lopez  * hp_exit_enumeration_attributes() - Clear all attribute data
4386b2770bfSJorge Lopez  *
4396b2770bfSJorge Lopez  * Clears all data allocated for this group of attributes
4406b2770bfSJorge Lopez  */
hp_exit_enumeration_attributes(void)4416b2770bfSJorge Lopez void hp_exit_enumeration_attributes(void)
4426b2770bfSJorge Lopez {
4436b2770bfSJorge Lopez 	int instance_id;
4446b2770bfSJorge Lopez 
4456b2770bfSJorge Lopez 	for (instance_id = 0; instance_id < bioscfg_drv.enumeration_instances_count;
4466b2770bfSJorge Lopez 	     instance_id++) {
4476b2770bfSJorge Lopez 		struct enumeration_data *enum_data = &bioscfg_drv.enumeration_data[instance_id];
4486b2770bfSJorge Lopez 		struct kobject *attr_name_kobj = enum_data->attr_name_kobj;
4496b2770bfSJorge Lopez 
4506b2770bfSJorge Lopez 		if (attr_name_kobj)
4516b2770bfSJorge Lopez 			sysfs_remove_group(attr_name_kobj, &enumeration_attr_group);
4526b2770bfSJorge Lopez 	}
4536b2770bfSJorge Lopez 	bioscfg_drv.enumeration_instances_count = 0;
4546b2770bfSJorge Lopez 
4556b2770bfSJorge Lopez 	kfree(bioscfg_drv.enumeration_data);
4566b2770bfSJorge Lopez 	bioscfg_drv.enumeration_data = NULL;
4576b2770bfSJorge Lopez }
458