1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/init.h>
14#include <linux/export.h>
15#include <linux/module.h>
16#include <linux/device.h>
17#include <linux/platform_device.h>
18#include <linux/io.h>
19#include <linux/acpi.h>
20#include <linux/uaccess.h>
21#include <linux/miscdevice.h>
22#include "acpi_thermal_rel.h"
23
24static acpi_handle acpi_thermal_rel_handle;
25static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock);
26static int acpi_thermal_rel_chrdev_count;
27static int acpi_thermal_rel_chrdev_exclu;
28
29static int acpi_thermal_rel_open(struct inode *inode, struct file *file)
30{
31 spin_lock(&acpi_thermal_rel_chrdev_lock);
32 if (acpi_thermal_rel_chrdev_exclu ||
33 (acpi_thermal_rel_chrdev_count && (file->f_flags & O_EXCL))) {
34 spin_unlock(&acpi_thermal_rel_chrdev_lock);
35 return -EBUSY;
36 }
37
38 if (file->f_flags & O_EXCL)
39 acpi_thermal_rel_chrdev_exclu = 1;
40 acpi_thermal_rel_chrdev_count++;
41
42 spin_unlock(&acpi_thermal_rel_chrdev_lock);
43
44 return nonseekable_open(inode, file);
45}
46
47static int acpi_thermal_rel_release(struct inode *inode, struct file *file)
48{
49 spin_lock(&acpi_thermal_rel_chrdev_lock);
50 acpi_thermal_rel_chrdev_count--;
51 acpi_thermal_rel_chrdev_exclu = 0;
52 spin_unlock(&acpi_thermal_rel_chrdev_lock);
53
54 return 0;
55}
56
57
58
59
60
61
62
63
64
65
66int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp,
67 bool create_dev)
68{
69 acpi_status status;
70 int result = 0;
71 int i;
72 int nr_bad_entries = 0;
73 struct trt *trts;
74 struct acpi_device *adev;
75 union acpi_object *p;
76 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
77 struct acpi_buffer element = { 0, NULL };
78 struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" };
79
80 if (!acpi_has_method(handle, "_TRT"))
81 return -ENODEV;
82
83 status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer);
84 if (ACPI_FAILURE(status))
85 return -ENODEV;
86
87 p = buffer.pointer;
88 if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
89 pr_err("Invalid _TRT data\n");
90 result = -EFAULT;
91 goto end;
92 }
93
94 *trt_count = p->package.count;
95 trts = kcalloc(*trt_count, sizeof(struct trt), GFP_KERNEL);
96 if (!trts) {
97 result = -ENOMEM;
98 goto end;
99 }
100
101 for (i = 0; i < *trt_count; i++) {
102 struct trt *trt = &trts[i - nr_bad_entries];
103
104 element.length = sizeof(struct trt);
105 element.pointer = trt;
106
107 status = acpi_extract_package(&(p->package.elements[i]),
108 &trt_format, &element);
109 if (ACPI_FAILURE(status)) {
110 nr_bad_entries++;
111 pr_warn("_TRT package %d is invalid, ignored\n", i);
112 continue;
113 }
114 if (!create_dev)
115 continue;
116
117 result = acpi_bus_get_device(trt->source, &adev);
118 if (result)
119 pr_warn("Failed to get source ACPI device\n");
120
121 result = acpi_bus_get_device(trt->target, &adev);
122 if (result)
123 pr_warn("Failed to get target ACPI device\n");
124 }
125
126 result = 0;
127
128 *trtp = trts;
129
130 *trt_count -= nr_bad_entries;
131end:
132 kfree(buffer.pointer);
133 return result;
134}
135EXPORT_SYMBOL(acpi_parse_trt);
136
137
138
139
140
141
142
143
144
145
146int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
147 bool create_dev)
148{
149 acpi_status status;
150 int result = 0;
151 int i;
152 int nr_bad_entries = 0;
153 struct art *arts;
154 struct acpi_device *adev;
155 union acpi_object *p;
156 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
157 struct acpi_buffer element = { 0, NULL };
158 struct acpi_buffer art_format = {
159 sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
160
161 if (!acpi_has_method(handle, "_ART"))
162 return -ENODEV;
163
164 status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
165 if (ACPI_FAILURE(status))
166 return -ENODEV;
167
168 p = buffer.pointer;
169 if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
170 pr_err("Invalid _ART data\n");
171 result = -EFAULT;
172 goto end;
173 }
174
175
176 *art_count = p->package.count - 1;
177 arts = kcalloc(*art_count, sizeof(struct art), GFP_KERNEL);
178 if (!arts) {
179 result = -ENOMEM;
180 goto end;
181 }
182
183 for (i = 0; i < *art_count; i++) {
184 struct art *art = &arts[i - nr_bad_entries];
185
186 element.length = sizeof(struct art);
187 element.pointer = art;
188
189 status = acpi_extract_package(&(p->package.elements[i + 1]),
190 &art_format, &element);
191 if (ACPI_FAILURE(status)) {
192 pr_warn("_ART package %d is invalid, ignored", i);
193 nr_bad_entries++;
194 continue;
195 }
196 if (!create_dev)
197 continue;
198
199 if (art->source) {
200 result = acpi_bus_get_device(art->source, &adev);
201 if (result)
202 pr_warn("Failed to get source ACPI device\n");
203 }
204 if (art->target) {
205 result = acpi_bus_get_device(art->target, &adev);
206 if (result)
207 pr_warn("Failed to get target ACPI device\n");
208 }
209 }
210
211 *artp = arts;
212
213 *art_count -= nr_bad_entries;
214end:
215 kfree(buffer.pointer);
216 return result;
217}
218EXPORT_SYMBOL(acpi_parse_art);
219
220
221
222static void get_single_name(acpi_handle handle, char *name)
223{
224 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};
225
226 if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)))
227 pr_warn("Failed to get device name from acpi handle\n");
228 else {
229 memcpy(name, buffer.pointer, ACPI_NAMESEG_SIZE);
230 kfree(buffer.pointer);
231 }
232}
233
234static int fill_art(char __user *ubuf)
235{
236 int i;
237 int ret;
238 int count;
239 int art_len;
240 struct art *arts = NULL;
241 union art_object *art_user;
242
243 ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false);
244 if (ret)
245 goto free_art;
246 art_len = count * sizeof(union art_object);
247 art_user = kzalloc(art_len, GFP_KERNEL);
248 if (!art_user) {
249 ret = -ENOMEM;
250 goto free_art;
251 }
252
253 for (i = 0; i < count; i++) {
254
255 get_single_name(arts[i].source, art_user[i].source_device);
256 get_single_name(arts[i].target, art_user[i].target_device);
257
258 memcpy(&art_user[i].weight, &arts[i].weight,
259 sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2));
260 }
261
262 if (copy_to_user(ubuf, art_user, art_len))
263 ret = -EFAULT;
264 kfree(art_user);
265free_art:
266 kfree(arts);
267 return ret;
268}
269
270static int fill_trt(char __user *ubuf)
271{
272 int i;
273 int ret;
274 int count;
275 int trt_len;
276 struct trt *trts = NULL;
277 union trt_object *trt_user;
278
279 ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false);
280 if (ret)
281 goto free_trt;
282 trt_len = count * sizeof(union trt_object);
283 trt_user = kzalloc(trt_len, GFP_KERNEL);
284 if (!trt_user) {
285 ret = -ENOMEM;
286 goto free_trt;
287 }
288
289 for (i = 0; i < count; i++) {
290
291 get_single_name(trts[i].source, trt_user[i].source_device);
292 get_single_name(trts[i].target, trt_user[i].target_device);
293 trt_user[i].sample_period = trts[i].sample_period;
294 trt_user[i].influence = trts[i].influence;
295 }
296
297 if (copy_to_user(ubuf, trt_user, trt_len))
298 ret = -EFAULT;
299 kfree(trt_user);
300free_trt:
301 kfree(trts);
302 return ret;
303}
304
305static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
306 unsigned long __arg)
307{
308 int ret = 0;
309 unsigned long length = 0;
310 int count = 0;
311 char __user *arg = (void __user *)__arg;
312 struct trt *trts = NULL;
313 struct art *arts = NULL;
314
315 switch (cmd) {
316 case ACPI_THERMAL_GET_TRT_COUNT:
317 ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,
318 &trts, false);
319 kfree(trts);
320 if (!ret)
321 return put_user(count, (unsigned long __user *)__arg);
322 return ret;
323 case ACPI_THERMAL_GET_TRT_LEN:
324 ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,
325 &trts, false);
326 kfree(trts);
327 length = count * sizeof(union trt_object);
328 if (!ret)
329 return put_user(length, (unsigned long __user *)__arg);
330 return ret;
331 case ACPI_THERMAL_GET_TRT:
332 return fill_trt(arg);
333 case ACPI_THERMAL_GET_ART_COUNT:
334 ret = acpi_parse_art(acpi_thermal_rel_handle, &count,
335 &arts, false);
336 kfree(arts);
337 if (!ret)
338 return put_user(count, (unsigned long __user *)__arg);
339 return ret;
340 case ACPI_THERMAL_GET_ART_LEN:
341 ret = acpi_parse_art(acpi_thermal_rel_handle, &count,
342 &arts, false);
343 kfree(arts);
344 length = count * sizeof(union art_object);
345 if (!ret)
346 return put_user(length, (unsigned long __user *)__arg);
347 return ret;
348
349 case ACPI_THERMAL_GET_ART:
350 return fill_art(arg);
351
352 default:
353 return -ENOTTY;
354 }
355}
356
357static const struct file_operations acpi_thermal_rel_fops = {
358 .owner = THIS_MODULE,
359 .open = acpi_thermal_rel_open,
360 .release = acpi_thermal_rel_release,
361 .unlocked_ioctl = acpi_thermal_rel_ioctl,
362 .llseek = no_llseek,
363};
364
365static struct miscdevice acpi_thermal_rel_misc_device = {
366 .minor = MISC_DYNAMIC_MINOR,
367 "acpi_thermal_rel",
368 &acpi_thermal_rel_fops
369};
370
371int acpi_thermal_rel_misc_device_add(acpi_handle handle)
372{
373 acpi_thermal_rel_handle = handle;
374
375 return misc_register(&acpi_thermal_rel_misc_device);
376}
377EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add);
378
379int acpi_thermal_rel_misc_device_remove(acpi_handle handle)
380{
381 misc_deregister(&acpi_thermal_rel_misc_device);
382
383 return 0;
384}
385EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove);
386
387MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
388MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com");
389MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver");
390MODULE_LICENSE("GPL v2");
391