1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /****************************************************************************** |
3 | * |
4 | * Module Name: utids - support for device Ids - HID, UID, CID, SUB, CLS |
5 | * |
6 | * Copyright (C) 2000 - 2023, Intel Corp. |
7 | * |
8 | *****************************************************************************/ |
9 | |
10 | #include <acpi/acpi.h> |
11 | #include "accommon.h" |
12 | #include "acinterp.h" |
13 | |
14 | #define _COMPONENT ACPI_UTILITIES |
15 | ACPI_MODULE_NAME("utids" ) |
16 | |
17 | /******************************************************************************* |
18 | * |
19 | * FUNCTION: acpi_ut_execute_HID |
20 | * |
21 | * PARAMETERS: device_node - Node for the device |
22 | * return_id - Where the string HID is returned |
23 | * |
24 | * RETURN: Status |
25 | * |
26 | * DESCRIPTION: Executes the _HID control method that returns the hardware |
27 | * ID of the device. The HID is either an 32-bit encoded EISAID |
28 | * Integer or a String. A string is always returned. An EISAID |
29 | * is converted to a string. |
30 | * |
31 | * NOTE: Internal function, no parameter validation |
32 | * |
33 | ******************************************************************************/ |
34 | acpi_status |
35 | acpi_ut_execute_HID(struct acpi_namespace_node *device_node, |
36 | struct acpi_pnp_device_id **return_id) |
37 | { |
38 | union acpi_operand_object *obj_desc; |
39 | struct acpi_pnp_device_id *hid; |
40 | u32 length; |
41 | acpi_status status; |
42 | |
43 | ACPI_FUNCTION_TRACE(ut_execute_HID); |
44 | |
45 | status = acpi_ut_evaluate_object(prefix_node: device_node, METHOD_NAME__HID, |
46 | ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, |
47 | return_desc: &obj_desc); |
48 | if (ACPI_FAILURE(status)) { |
49 | return_ACPI_STATUS(status); |
50 | } |
51 | |
52 | /* Get the size of the String to be returned, includes null terminator */ |
53 | |
54 | if (obj_desc->common.type == ACPI_TYPE_INTEGER) { |
55 | length = ACPI_EISAID_STRING_SIZE; |
56 | } else { |
57 | length = obj_desc->string.length + 1; |
58 | } |
59 | |
60 | /* Allocate a buffer for the HID */ |
61 | |
62 | hid = |
63 | ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pnp_device_id) + |
64 | (acpi_size)length); |
65 | if (!hid) { |
66 | status = AE_NO_MEMORY; |
67 | goto cleanup; |
68 | } |
69 | |
70 | /* Area for the string starts after PNP_DEVICE_ID struct */ |
71 | |
72 | hid->string = |
73 | ACPI_ADD_PTR(char, hid, sizeof(struct acpi_pnp_device_id)); |
74 | |
75 | /* Convert EISAID to a string or simply copy existing string */ |
76 | |
77 | if (obj_desc->common.type == ACPI_TYPE_INTEGER) { |
78 | acpi_ex_eisa_id_to_string(dest: hid->string, compressed_id: obj_desc->integer.value); |
79 | } else { |
80 | strcpy(p: hid->string, q: obj_desc->string.pointer); |
81 | } |
82 | |
83 | hid->length = length; |
84 | *return_id = hid; |
85 | |
86 | cleanup: |
87 | |
88 | /* On exit, we must delete the return object */ |
89 | |
90 | acpi_ut_remove_reference(object: obj_desc); |
91 | return_ACPI_STATUS(status); |
92 | } |
93 | |
94 | /******************************************************************************* |
95 | * |
96 | * FUNCTION: acpi_ut_execute_UID |
97 | * |
98 | * PARAMETERS: device_node - Node for the device |
99 | * return_id - Where the string UID is returned |
100 | * |
101 | * RETURN: Status |
102 | * |
103 | * DESCRIPTION: Executes the _UID control method that returns the unique |
104 | * ID of the device. The UID is either a 64-bit Integer (NOT an |
105 | * EISAID) or a string. Always returns a string. A 64-bit integer |
106 | * is converted to a decimal string. |
107 | * |
108 | * NOTE: Internal function, no parameter validation |
109 | * |
110 | ******************************************************************************/ |
111 | |
112 | acpi_status |
113 | acpi_ut_execute_UID(struct acpi_namespace_node *device_node, |
114 | struct acpi_pnp_device_id **return_id) |
115 | { |
116 | union acpi_operand_object *obj_desc; |
117 | struct acpi_pnp_device_id *uid; |
118 | u32 length; |
119 | acpi_status status; |
120 | |
121 | ACPI_FUNCTION_TRACE(ut_execute_UID); |
122 | |
123 | status = acpi_ut_evaluate_object(prefix_node: device_node, METHOD_NAME__UID, |
124 | ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, |
125 | return_desc: &obj_desc); |
126 | if (ACPI_FAILURE(status)) { |
127 | return_ACPI_STATUS(status); |
128 | } |
129 | |
130 | /* Get the size of the String to be returned, includes null terminator */ |
131 | |
132 | if (obj_desc->common.type == ACPI_TYPE_INTEGER) { |
133 | length = ACPI_MAX64_DECIMAL_DIGITS + 1; |
134 | } else { |
135 | length = obj_desc->string.length + 1; |
136 | } |
137 | |
138 | /* Allocate a buffer for the UID */ |
139 | |
140 | uid = |
141 | ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pnp_device_id) + |
142 | (acpi_size)length); |
143 | if (!uid) { |
144 | status = AE_NO_MEMORY; |
145 | goto cleanup; |
146 | } |
147 | |
148 | /* Area for the string starts after PNP_DEVICE_ID struct */ |
149 | |
150 | uid->string = |
151 | ACPI_ADD_PTR(char, uid, sizeof(struct acpi_pnp_device_id)); |
152 | |
153 | /* Convert an Integer to string, or just copy an existing string */ |
154 | |
155 | if (obj_desc->common.type == ACPI_TYPE_INTEGER) { |
156 | acpi_ex_integer_to_string(dest: uid->string, value: obj_desc->integer.value); |
157 | } else { |
158 | strcpy(p: uid->string, q: obj_desc->string.pointer); |
159 | } |
160 | |
161 | uid->length = length; |
162 | *return_id = uid; |
163 | |
164 | cleanup: |
165 | |
166 | /* On exit, we must delete the return object */ |
167 | |
168 | acpi_ut_remove_reference(object: obj_desc); |
169 | return_ACPI_STATUS(status); |
170 | } |
171 | |
172 | /******************************************************************************* |
173 | * |
174 | * FUNCTION: acpi_ut_execute_CID |
175 | * |
176 | * PARAMETERS: device_node - Node for the device |
177 | * return_cid_list - Where the CID list is returned |
178 | * |
179 | * RETURN: Status, list of CID strings |
180 | * |
181 | * DESCRIPTION: Executes the _CID control method that returns one or more |
182 | * compatible hardware IDs for the device. |
183 | * |
184 | * NOTE: Internal function, no parameter validation |
185 | * |
186 | * A _CID method can return either a single compatible ID or a package of |
187 | * compatible IDs. Each compatible ID can be one of the following: |
188 | * 1) Integer (32 bit compressed EISA ID) or |
189 | * 2) String (PCI ID format, e.g. "PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss") |
190 | * |
191 | * The Integer CIDs are converted to string format by this function. |
192 | * |
193 | ******************************************************************************/ |
194 | |
195 | acpi_status |
196 | acpi_ut_execute_CID(struct acpi_namespace_node *device_node, |
197 | struct acpi_pnp_device_id_list **return_cid_list) |
198 | { |
199 | union acpi_operand_object **cid_objects; |
200 | union acpi_operand_object *obj_desc; |
201 | struct acpi_pnp_device_id_list *cid_list; |
202 | char *next_id_string; |
203 | u32 string_area_size; |
204 | u32 length; |
205 | u32 cid_list_size; |
206 | acpi_status status; |
207 | u32 count; |
208 | u32 i; |
209 | |
210 | ACPI_FUNCTION_TRACE(ut_execute_CID); |
211 | |
212 | /* Evaluate the _CID method for this device */ |
213 | |
214 | status = acpi_ut_evaluate_object(prefix_node: device_node, METHOD_NAME__CID, |
215 | ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING |
216 | | ACPI_BTYPE_PACKAGE, return_desc: &obj_desc); |
217 | if (ACPI_FAILURE(status)) { |
218 | return_ACPI_STATUS(status); |
219 | } |
220 | |
221 | /* |
222 | * Get the count and size of the returned _CIDs. _CID can return either |
223 | * a Package of Integers/Strings or a single Integer or String. |
224 | * Note: This section also validates that all CID elements are of the |
225 | * correct type (Integer or String). |
226 | */ |
227 | if (obj_desc->common.type == ACPI_TYPE_PACKAGE) { |
228 | count = obj_desc->package.count; |
229 | cid_objects = obj_desc->package.elements; |
230 | } else { /* Single Integer or String CID */ |
231 | |
232 | count = 1; |
233 | cid_objects = &obj_desc; |
234 | } |
235 | |
236 | string_area_size = 0; |
237 | for (i = 0; i < count; i++) { |
238 | |
239 | /* String lengths include null terminator */ |
240 | |
241 | switch (cid_objects[i]->common.type) { |
242 | case ACPI_TYPE_INTEGER: |
243 | |
244 | string_area_size += ACPI_EISAID_STRING_SIZE; |
245 | break; |
246 | |
247 | case ACPI_TYPE_STRING: |
248 | |
249 | string_area_size += cid_objects[i]->string.length + 1; |
250 | break; |
251 | |
252 | default: |
253 | |
254 | status = AE_TYPE; |
255 | goto cleanup; |
256 | } |
257 | } |
258 | |
259 | /* |
260 | * Now that we know the length of the CIDs, allocate return buffer: |
261 | * 1) Size of the base structure + |
262 | * 2) Size of the CID PNP_DEVICE_ID array + |
263 | * 3) Size of the actual CID strings |
264 | */ |
265 | cid_list_size = sizeof(struct acpi_pnp_device_id_list) + |
266 | (count * sizeof(struct acpi_pnp_device_id)) + string_area_size; |
267 | |
268 | cid_list = ACPI_ALLOCATE_ZEROED(cid_list_size); |
269 | if (!cid_list) { |
270 | status = AE_NO_MEMORY; |
271 | goto cleanup; |
272 | } |
273 | |
274 | /* Area for CID strings starts after the CID PNP_DEVICE_ID array */ |
275 | |
276 | next_id_string = ACPI_CAST_PTR(char, cid_list->ids) + |
277 | ((acpi_size)count * sizeof(struct acpi_pnp_device_id)); |
278 | |
279 | /* Copy/convert the CIDs to the return buffer */ |
280 | |
281 | for (i = 0; i < count; i++) { |
282 | if (cid_objects[i]->common.type == ACPI_TYPE_INTEGER) { |
283 | |
284 | /* Convert the Integer (EISAID) CID to a string */ |
285 | |
286 | acpi_ex_eisa_id_to_string(dest: next_id_string, |
287 | compressed_id: cid_objects[i]->integer. |
288 | value); |
289 | length = ACPI_EISAID_STRING_SIZE; |
290 | } else { /* ACPI_TYPE_STRING */ |
291 | /* Copy the String CID from the returned object */ |
292 | strcpy(p: next_id_string, q: cid_objects[i]->string.pointer); |
293 | length = cid_objects[i]->string.length + 1; |
294 | } |
295 | |
296 | cid_list->ids[i].string = next_id_string; |
297 | cid_list->ids[i].length = length; |
298 | next_id_string += length; |
299 | } |
300 | |
301 | /* Finish the CID list */ |
302 | |
303 | cid_list->count = count; |
304 | cid_list->list_size = cid_list_size; |
305 | *return_cid_list = cid_list; |
306 | |
307 | cleanup: |
308 | |
309 | /* On exit, we must delete the _CID return object */ |
310 | |
311 | acpi_ut_remove_reference(object: obj_desc); |
312 | return_ACPI_STATUS(status); |
313 | } |
314 | |
315 | /******************************************************************************* |
316 | * |
317 | * FUNCTION: acpi_ut_execute_CLS |
318 | * |
319 | * PARAMETERS: device_node - Node for the device |
320 | * return_id - Where the _CLS is returned |
321 | * |
322 | * RETURN: Status |
323 | * |
324 | * DESCRIPTION: Executes the _CLS control method that returns PCI-defined |
325 | * class code of the device. The _CLS value is always a package |
326 | * containing PCI class information as a list of integers. |
327 | * The returned string has format "BBSSPP", where: |
328 | * BB = Base-class code |
329 | * SS = Sub-class code |
330 | * PP = Programming Interface code |
331 | * |
332 | ******************************************************************************/ |
333 | |
334 | acpi_status |
335 | acpi_ut_execute_CLS(struct acpi_namespace_node *device_node, |
336 | struct acpi_pnp_device_id **return_id) |
337 | { |
338 | union acpi_operand_object *obj_desc; |
339 | union acpi_operand_object **cls_objects; |
340 | u32 count; |
341 | struct acpi_pnp_device_id *cls; |
342 | u32 length; |
343 | acpi_status status; |
344 | u8 class_code[3] = { 0, 0, 0 }; |
345 | |
346 | ACPI_FUNCTION_TRACE(ut_execute_CLS); |
347 | |
348 | status = acpi_ut_evaluate_object(prefix_node: device_node, METHOD_NAME__CLS, |
349 | ACPI_BTYPE_PACKAGE, return_desc: &obj_desc); |
350 | if (ACPI_FAILURE(status)) { |
351 | return_ACPI_STATUS(status); |
352 | } |
353 | |
354 | /* Get the size of the String to be returned, includes null terminator */ |
355 | |
356 | length = ACPI_PCICLS_STRING_SIZE; |
357 | cls_objects = obj_desc->package.elements; |
358 | count = obj_desc->package.count; |
359 | |
360 | if (obj_desc->common.type == ACPI_TYPE_PACKAGE) { |
361 | if (count > 0 |
362 | && cls_objects[0]->common.type == ACPI_TYPE_INTEGER) { |
363 | class_code[0] = (u8)cls_objects[0]->integer.value; |
364 | } |
365 | if (count > 1 |
366 | && cls_objects[1]->common.type == ACPI_TYPE_INTEGER) { |
367 | class_code[1] = (u8)cls_objects[1]->integer.value; |
368 | } |
369 | if (count > 2 |
370 | && cls_objects[2]->common.type == ACPI_TYPE_INTEGER) { |
371 | class_code[2] = (u8)cls_objects[2]->integer.value; |
372 | } |
373 | } |
374 | |
375 | /* Allocate a buffer for the CLS */ |
376 | |
377 | cls = |
378 | ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_pnp_device_id) + |
379 | (acpi_size)length); |
380 | if (!cls) { |
381 | status = AE_NO_MEMORY; |
382 | goto cleanup; |
383 | } |
384 | |
385 | /* Area for the string starts after PNP_DEVICE_ID struct */ |
386 | |
387 | cls->string = |
388 | ACPI_ADD_PTR(char, cls, sizeof(struct acpi_pnp_device_id)); |
389 | |
390 | /* Simply copy existing string */ |
391 | |
392 | acpi_ex_pci_cls_to_string(dest: cls->string, class_code); |
393 | cls->length = length; |
394 | *return_id = cls; |
395 | |
396 | cleanup: |
397 | |
398 | /* On exit, we must delete the return object */ |
399 | |
400 | acpi_ut_remove_reference(object: obj_desc); |
401 | return_ACPI_STATUS(status); |
402 | } |
403 | |