1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#include <linux/acpi.h>
19#include <linux/efi.h>
20#include <linux/pci.h>
21
22struct acpi_hid_uid {
23 struct acpi_device_id hid[2];
24 char uid[11];
25};
26
27static int __init match_acpi_dev(struct device *dev, void *data)
28{
29 struct acpi_hid_uid hid_uid = *(struct acpi_hid_uid *)data;
30 struct acpi_device *adev = to_acpi_device(dev);
31
32 if (acpi_match_device_ids(adev, hid_uid.hid))
33 return 0;
34
35 if (adev->pnp.unique_id)
36 return !strcmp(adev->pnp.unique_id, hid_uid.uid);
37 else
38 return !strcmp("0", hid_uid.uid);
39}
40
41static long __init parse_acpi_path(struct efi_dev_path *node,
42 struct device *parent, struct device **child)
43{
44 struct acpi_hid_uid hid_uid = {};
45 struct device *phys_dev;
46
47 if (node->length != 12)
48 return -EINVAL;
49
50 sprintf(hid_uid.hid[0].id, "%c%c%c%04X",
51 'A' + ((node->acpi.hid >> 10) & 0x1f) - 1,
52 'A' + ((node->acpi.hid >> 5) & 0x1f) - 1,
53 'A' + ((node->acpi.hid >> 0) & 0x1f) - 1,
54 node->acpi.hid >> 16);
55 sprintf(hid_uid.uid, "%u", node->acpi.uid);
56
57 *child = bus_find_device(&acpi_bus_type, NULL, &hid_uid,
58 match_acpi_dev);
59 if (!*child)
60 return -ENODEV;
61
62 phys_dev = acpi_get_first_physical_node(to_acpi_device(*child));
63 if (phys_dev) {
64 get_device(phys_dev);
65 put_device(*child);
66 *child = phys_dev;
67 }
68
69 return 0;
70}
71
72static int __init match_pci_dev(struct device *dev, void *data)
73{
74 unsigned int devfn = *(unsigned int *)data;
75
76 return dev_is_pci(dev) && to_pci_dev(dev)->devfn == devfn;
77}
78
79static long __init parse_pci_path(struct efi_dev_path *node,
80 struct device *parent, struct device **child)
81{
82 unsigned int devfn;
83
84 if (node->length != 6)
85 return -EINVAL;
86 if (!parent)
87 return -EINVAL;
88
89 devfn = PCI_DEVFN(node->pci.dev, node->pci.fn);
90
91 *child = device_find_child(parent, &devfn, match_pci_dev);
92 if (!*child)
93 return -ENODEV;
94
95 return 0;
96}
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115static long __init parse_end_path(struct efi_dev_path *node,
116 struct device *parent, struct device **child)
117{
118 if (node->length != 4)
119 return -EINVAL;
120 if (node->sub_type != EFI_DEV_END_INSTANCE &&
121 node->sub_type != EFI_DEV_END_ENTIRE)
122 return -EINVAL;
123 if (!parent)
124 return -ENODEV;
125
126 *child = get_device(parent);
127 return node->sub_type;
128}
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166struct device * __init efi_get_device_by_path(struct efi_dev_path **node,
167 size_t *len)
168{
169 struct device *parent = NULL, *child;
170 long ret = 0;
171
172 if (!*len)
173 return NULL;
174
175 while (!ret) {
176 if (*len < 4 || *len < (*node)->length)
177 ret = -EINVAL;
178 else if ((*node)->type == EFI_DEV_ACPI &&
179 (*node)->sub_type == EFI_DEV_BASIC_ACPI)
180 ret = parse_acpi_path(*node, parent, &child);
181 else if ((*node)->type == EFI_DEV_HW &&
182 (*node)->sub_type == EFI_DEV_PCI)
183 ret = parse_pci_path(*node, parent, &child);
184 else if (((*node)->type == EFI_DEV_END_PATH ||
185 (*node)->type == EFI_DEV_END_PATH2))
186 ret = parse_end_path(*node, parent, &child);
187 else
188 ret = -ENOTSUPP;
189
190 put_device(parent);
191 if (ret < 0)
192 return ERR_PTR(ret);
193
194 parent = child;
195 *node = (void *)*node + (*node)->length;
196 *len -= (*node)->length;
197 }
198
199 if (ret == EFI_DEV_END_ENTIRE)
200 *len = 0;
201
202 return child;
203}
204