aboutsummaryrefslogtreecommitdiff
path: root/drivers/acpi/acpi_keyvalue.c
blob: 5c3cde921b228c6b1a3c6e48ec2f1fa01d3c132a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
 * Key/Value handler from _DSM method
 *
 * Copyright (C) 2013 Linaro Ltd
 *
 * Author: Graeme Gregory <graeme.gregory@linaro.org>
 *
 * Original based on code :-
 *
 * Copyright (C) 2013 Advanced Micro Devices, Inc.
 *
 * Author: Brandon Anderson <brandon.anderson@amd.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/acpi.h>

/* UUID: a706b112-bf0b-48d2-9fa3-95591a3c4c06 (randomly generated) */
static const char acpi_amba_dsm_uuid[] = {
	0xa7, 0x06, 0xb1, 0x12, 0xbf, 0x0b, 0x48, 0xd2,
	0x9f, 0xa3, 0x95, 0x59, 0x1a, 0x3c, 0x4c, 0x06
};

/* acpi_dsm_lookup_value()
 *
 * Helper to parse through ACPI _DSM object for a device. Each entry
 * has three fields.
 */
int acpi_dsm_lookup_value(acpi_handle handle,
		const char *tag, int index,
		struct acpi_dsm_entry *entry)
{
	acpi_status status;
	struct acpi_object_list input;
	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object params[4];
	union acpi_object *obj;
	int len, match_count, i;

	/* invalidate output in case there's no entry to supply */
	entry->key = NULL;
	entry->value = NULL;

	if (!acpi_has_method(handle, "_DSM"))
		return -ENOENT;

	input.count = 4;
	params[0].type = ACPI_TYPE_BUFFER;		/* UUID */
	params[0].buffer.length = sizeof(acpi_amba_dsm_uuid);
	params[0].buffer.pointer = (char *)acpi_amba_dsm_uuid;
	params[1].type = ACPI_TYPE_INTEGER;		/* Revision */
	params[1].integer.value = 1;
	params[2].type = ACPI_TYPE_INTEGER;		/* Function # */
	params[2].integer.value = 1;
	params[3].type = ACPI_TYPE_PACKAGE;		/* Arguments */
	params[3].package.count = 0;
	params[3].package.elements = NULL;
	input.pointer = params;

	status = acpi_evaluate_object_typed(handle, "_DSM",
			&input, &output, ACPI_TYPE_PACKAGE);
	if (ACPI_FAILURE(status)) {
		pr_err("failed to get _DSM package for this device\n");
		return -ENOENT;
	}

	obj = (union acpi_object *)output.pointer;

	/* parse 2 objects per entry */
	match_count = 0;
	for (i = 0; (i + 2) <= obj->package.count; i += 2) {
		/* key must be a string */
		len = obj->package.elements[i].string.length;
		if (len <= 0)
			continue;

		/* check to see if this is the entry to return */
		if (strncmp(tag, obj->package.elements[i].string.pointer,
				len) != 0 ||
				match_count < index) {
			match_count++;
			continue;
		}

		/* copy the key */
		entry->key = kmalloc(len + 1, GFP_KERNEL);
		strncpy(entry->key,
				obj->package.elements[i].string.pointer,
				len);
		entry->key[len] = '\0';

		/* value is a string with space-delimited fields if necessary */
		len = obj->package.elements[i + 1].string.length;
		if (len > 0) {
			entry->value = kmalloc(len + 1, GFP_KERNEL);
			strncpy(entry->value,
				obj->package.elements[i+1].string.pointer,
				len);
			entry->value[len] = '\0';
		}

		break;
	}

	kfree(output.pointer);

	if (entry->key == NULL)
		return -ENOENT;

	return 0;
}
EXPORT_SYMBOL_GPL(acpi_dsm_lookup_value);