1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Functions corresponding to enumeration type attributes under
4 * BIOS Enumeration GUID for use with dell-wmi-sysman
5 *
6 * Copyright (c) 2020 Dell Inc.
7 */
8
9 #include "dell-wmi-sysman.h"
10
11 get_instance_id(enumeration);
12
current_value_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)13 static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
14 {
15 int instance_id = get_enumeration_instance_id(kobj);
16 union acpi_object *obj;
17 ssize_t ret;
18
19 if (instance_id < 0)
20 return instance_id;
21
22 /* need to use specific instance_id and guid combination to get right data */
23 obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID);
24 if (!obj)
25 return -EIO;
26 if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count < ENUM_MIN_ELEMENTS ||
27 obj->package.elements[CURRENT_VAL].type != ACPI_TYPE_STRING) {
28 kfree(obj);
29 return -EIO;
30 }
31 ret = snprintf(buf, PAGE_SIZE, "%s\n", obj->package.elements[CURRENT_VAL].string.pointer);
32 kfree(obj);
33 return ret;
34 }
35
36 /**
37 * validate_enumeration_input() - Validate input of current_value against possible values
38 * @instance_id: The instance on which input is validated
39 * @buf: Input value
40 */
validate_enumeration_input(int instance_id,const char * buf)41 static int validate_enumeration_input(int instance_id, const char *buf)
42 {
43 char *options, *tmp, *p;
44 int ret = -EINVAL;
45
46 options = tmp = kstrdup(wmi_priv.enumeration_data[instance_id].possible_values,
47 GFP_KERNEL);
48 if (!options)
49 return -ENOMEM;
50
51 while ((p = strsep(&options, ";")) != NULL) {
52 if (!*p)
53 continue;
54 if (!strcasecmp(p, buf)) {
55 ret = 0;
56 break;
57 }
58 }
59
60 kfree(tmp);
61 return ret;
62 }
63
64 attribute_s_property_show(display_name_language_code, enumeration);
65 static struct kobj_attribute displ_langcode =
66 __ATTR_RO(display_name_language_code);
67
68 attribute_s_property_show(display_name, enumeration);
69 static struct kobj_attribute displ_name =
70 __ATTR_RO(display_name);
71
72 attribute_s_property_show(default_value, enumeration);
73 static struct kobj_attribute default_val =
74 __ATTR_RO(default_value);
75
76 attribute_property_store(current_value, enumeration);
77 static struct kobj_attribute current_val =
78 __ATTR_RW_MODE(current_value, 0600);
79
80 attribute_s_property_show(dell_modifier, enumeration);
81 static struct kobj_attribute modifier =
82 __ATTR_RO(dell_modifier);
83
84 attribute_s_property_show(dell_value_modifier, enumeration);
85 static struct kobj_attribute value_modfr =
86 __ATTR_RO(dell_value_modifier);
87
88 attribute_s_property_show(possible_values, enumeration);
89 static struct kobj_attribute poss_val =
90 __ATTR_RO(possible_values);
91
type_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)92 static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
93 char *buf)
94 {
95 return sprintf(buf, "enumeration\n");
96 }
97 static struct kobj_attribute type =
98 __ATTR_RO(type);
99
100 static struct attribute *enumeration_attrs[] = {
101 &displ_langcode.attr,
102 &displ_name.attr,
103 &default_val.attr,
104 ¤t_val.attr,
105 &modifier.attr,
106 &value_modfr.attr,
107 &poss_val.attr,
108 &type.attr,
109 NULL,
110 };
111
112 static const struct attribute_group enumeration_attr_group = {
113 .attrs = enumeration_attrs,
114 };
115
alloc_enum_data(void)116 int alloc_enum_data(void)
117 {
118 int ret = 0;
119
120 wmi_priv.enumeration_instances_count =
121 get_instance_count(DELL_WMI_BIOS_ENUMERATION_ATTRIBUTE_GUID);
122 wmi_priv.enumeration_data = kcalloc(wmi_priv.enumeration_instances_count,
123 sizeof(struct enumeration_data), GFP_KERNEL);
124 if (!wmi_priv.enumeration_data) {
125 wmi_priv.enumeration_instances_count = 0;
126 ret = -ENOMEM;
127 }
128 return ret;
129 }
130
131 /**
132 * populate_enum_data() - Populate all properties of an instance under enumeration attribute
133 * @enumeration_obj: ACPI object with enumeration data
134 * @instance_id: The instance to enumerate
135 * @attr_name_kobj: The parent kernel object
136 * @enum_property_count: Total properties count under enumeration type
137 */
populate_enum_data(union acpi_object * enumeration_obj,int instance_id,struct kobject * attr_name_kobj,u32 enum_property_count)138 int populate_enum_data(union acpi_object *enumeration_obj, int instance_id,
139 struct kobject *attr_name_kobj, u32 enum_property_count)
140 {
141 int i, next_obj, value_modifier_count, possible_values_count;
142
143 wmi_priv.enumeration_data[instance_id].attr_name_kobj = attr_name_kobj;
144 if (check_property_type(enumeration, ATTR_NAME, ACPI_TYPE_STRING))
145 return -EINVAL;
146 strlcpy_attr(wmi_priv.enumeration_data[instance_id].attribute_name,
147 enumeration_obj[ATTR_NAME].string.pointer);
148 if (check_property_type(enumeration, DISPL_NAME_LANG_CODE, ACPI_TYPE_STRING))
149 return -EINVAL;
150 strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name_language_code,
151 enumeration_obj[DISPL_NAME_LANG_CODE].string.pointer);
152 if (check_property_type(enumeration, DISPLAY_NAME, ACPI_TYPE_STRING))
153 return -EINVAL;
154 strlcpy_attr(wmi_priv.enumeration_data[instance_id].display_name,
155 enumeration_obj[DISPLAY_NAME].string.pointer);
156 if (check_property_type(enumeration, DEFAULT_VAL, ACPI_TYPE_STRING))
157 return -EINVAL;
158 strlcpy_attr(wmi_priv.enumeration_data[instance_id].default_value,
159 enumeration_obj[DEFAULT_VAL].string.pointer);
160 if (check_property_type(enumeration, MODIFIER, ACPI_TYPE_STRING))
161 return -EINVAL;
162 strlcpy_attr(wmi_priv.enumeration_data[instance_id].dell_modifier,
163 enumeration_obj[MODIFIER].string.pointer);
164
165 next_obj = MODIFIER + 1;
166
167 if (next_obj >= enum_property_count)
168 return -EINVAL;
169
170 if (check_property_type(enumeration, next_obj, ACPI_TYPE_INTEGER))
171 return -EINVAL;
172 value_modifier_count = (uintptr_t)enumeration_obj[next_obj++].string.pointer;
173
174 for (i = 0; i < value_modifier_count; i++) {
175 if (next_obj >= enum_property_count)
176 return -EINVAL;
177 if (check_property_type(enumeration, next_obj, ACPI_TYPE_STRING))
178 return -EINVAL;
179 strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier,
180 enumeration_obj[next_obj++].string.pointer);
181 strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier, ";");
182 }
183
184 if (next_obj >= enum_property_count)
185 return -EINVAL;
186
187 if (check_property_type(enumeration, next_obj, ACPI_TYPE_INTEGER))
188 return -EINVAL;
189 possible_values_count = (uintptr_t) enumeration_obj[next_obj++].string.pointer;
190
191 for (i = 0; i < possible_values_count; i++) {
192 if (next_obj >= enum_property_count)
193 return -EINVAL;
194 if (check_property_type(enumeration, next_obj, ACPI_TYPE_STRING))
195 return -EINVAL;
196 strcat(wmi_priv.enumeration_data[instance_id].possible_values,
197 enumeration_obj[next_obj++].string.pointer);
198 strcat(wmi_priv.enumeration_data[instance_id].possible_values, ";");
199 }
200
201 return sysfs_create_group(attr_name_kobj, &enumeration_attr_group);
202 }
203
204 /**
205 * exit_enum_attributes() - Clear all attribute data
206 *
207 * Clears all data allocated for this group of attributes
208 */
exit_enum_attributes(void)209 void exit_enum_attributes(void)
210 {
211 int instance_id;
212
213 for (instance_id = 0; instance_id < wmi_priv.enumeration_instances_count; instance_id++) {
214 if (wmi_priv.enumeration_data[instance_id].attr_name_kobj)
215 sysfs_remove_group(wmi_priv.enumeration_data[instance_id].attr_name_kobj,
216 &enumeration_attr_group);
217 }
218 wmi_priv.enumeration_instances_count = 0;
219
220 kfree(wmi_priv.enumeration_data);
221 wmi_priv.enumeration_data = NULL;
222 }
223