0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/init.h>
0014 #include <linux/export.h>
0015 #include <linux/module.h>
0016 #include <linux/device.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/io.h>
0019 #include <linux/acpi.h>
0020 #include <linux/uaccess.h>
0021 #include <linux/miscdevice.h>
0022 #include <linux/fs.h>
0023 #include "acpi_thermal_rel.h"
0024
0025 static acpi_handle acpi_thermal_rel_handle;
0026 static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock);
0027 static int acpi_thermal_rel_chrdev_count;
0028 static int acpi_thermal_rel_chrdev_exclu;
0029
0030 static int acpi_thermal_rel_open(struct inode *inode, struct file *file)
0031 {
0032 spin_lock(&acpi_thermal_rel_chrdev_lock);
0033 if (acpi_thermal_rel_chrdev_exclu ||
0034 (acpi_thermal_rel_chrdev_count && (file->f_flags & O_EXCL))) {
0035 spin_unlock(&acpi_thermal_rel_chrdev_lock);
0036 return -EBUSY;
0037 }
0038
0039 if (file->f_flags & O_EXCL)
0040 acpi_thermal_rel_chrdev_exclu = 1;
0041 acpi_thermal_rel_chrdev_count++;
0042
0043 spin_unlock(&acpi_thermal_rel_chrdev_lock);
0044
0045 return nonseekable_open(inode, file);
0046 }
0047
0048 static int acpi_thermal_rel_release(struct inode *inode, struct file *file)
0049 {
0050 spin_lock(&acpi_thermal_rel_chrdev_lock);
0051 acpi_thermal_rel_chrdev_count--;
0052 acpi_thermal_rel_chrdev_exclu = 0;
0053 spin_unlock(&acpi_thermal_rel_chrdev_lock);
0054
0055 return 0;
0056 }
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067 int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp,
0068 bool create_dev)
0069 {
0070 acpi_status status;
0071 int result = 0;
0072 int i;
0073 int nr_bad_entries = 0;
0074 struct trt *trts;
0075 union acpi_object *p;
0076 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
0077 struct acpi_buffer element = { 0, NULL };
0078 struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" };
0079
0080 status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer);
0081 if (ACPI_FAILURE(status))
0082 return -ENODEV;
0083
0084 p = buffer.pointer;
0085 if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
0086 pr_err("Invalid _TRT data\n");
0087 result = -EFAULT;
0088 goto end;
0089 }
0090
0091 *trt_count = p->package.count;
0092 trts = kcalloc(*trt_count, sizeof(struct trt), GFP_KERNEL);
0093 if (!trts) {
0094 result = -ENOMEM;
0095 goto end;
0096 }
0097
0098 for (i = 0; i < *trt_count; i++) {
0099 struct trt *trt = &trts[i - nr_bad_entries];
0100
0101 element.length = sizeof(struct trt);
0102 element.pointer = trt;
0103
0104 status = acpi_extract_package(&(p->package.elements[i]),
0105 &trt_format, &element);
0106 if (ACPI_FAILURE(status)) {
0107 nr_bad_entries++;
0108 pr_warn("_TRT package %d is invalid, ignored\n", i);
0109 continue;
0110 }
0111 if (!create_dev)
0112 continue;
0113
0114 if (!acpi_fetch_acpi_dev(trt->source))
0115 pr_warn("Failed to get source ACPI device\n");
0116
0117 if (!acpi_fetch_acpi_dev(trt->target))
0118 pr_warn("Failed to get target ACPI device\n");
0119 }
0120
0121 result = 0;
0122
0123 *trtp = trts;
0124
0125 *trt_count -= nr_bad_entries;
0126 end:
0127 kfree(buffer.pointer);
0128 return result;
0129 }
0130 EXPORT_SYMBOL(acpi_parse_trt);
0131
0132
0133
0134
0135
0136
0137
0138
0139
0140
0141 int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
0142 bool create_dev)
0143 {
0144 acpi_status status;
0145 int result = 0;
0146 int i;
0147 int nr_bad_entries = 0;
0148 struct art *arts;
0149 union acpi_object *p;
0150 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
0151 struct acpi_buffer element = { 0, NULL };
0152 struct acpi_buffer art_format = {
0153 sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
0154
0155 status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
0156 if (ACPI_FAILURE(status))
0157 return -ENODEV;
0158
0159 p = buffer.pointer;
0160 if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
0161 pr_err("Invalid _ART data\n");
0162 result = -EFAULT;
0163 goto end;
0164 }
0165
0166
0167 *art_count = p->package.count - 1;
0168 arts = kcalloc(*art_count, sizeof(struct art), GFP_KERNEL);
0169 if (!arts) {
0170 result = -ENOMEM;
0171 goto end;
0172 }
0173
0174 for (i = 0; i < *art_count; i++) {
0175 struct art *art = &arts[i - nr_bad_entries];
0176
0177 element.length = sizeof(struct art);
0178 element.pointer = art;
0179
0180 status = acpi_extract_package(&(p->package.elements[i + 1]),
0181 &art_format, &element);
0182 if (ACPI_FAILURE(status)) {
0183 pr_warn("_ART package %d is invalid, ignored", i);
0184 nr_bad_entries++;
0185 continue;
0186 }
0187 if (!create_dev)
0188 continue;
0189
0190 if (!acpi_fetch_acpi_dev(art->source))
0191 pr_warn("Failed to get source ACPI device\n");
0192
0193 if (!acpi_fetch_acpi_dev(art->target))
0194 pr_warn("Failed to get target ACPI device\n");
0195 }
0196
0197 *artp = arts;
0198
0199 *art_count -= nr_bad_entries;
0200 end:
0201 kfree(buffer.pointer);
0202 return result;
0203 }
0204 EXPORT_SYMBOL(acpi_parse_art);
0205
0206
0207
0208 static void get_single_name(acpi_handle handle, char *name)
0209 {
0210 struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};
0211
0212 if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)))
0213 pr_warn("Failed to get device name from acpi handle\n");
0214 else {
0215 memcpy(name, buffer.pointer, ACPI_NAMESEG_SIZE);
0216 kfree(buffer.pointer);
0217 }
0218 }
0219
0220 static int fill_art(char __user *ubuf)
0221 {
0222 int i;
0223 int ret;
0224 int count;
0225 int art_len;
0226 struct art *arts = NULL;
0227 union art_object *art_user;
0228
0229 ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false);
0230 if (ret)
0231 goto free_art;
0232 art_len = count * sizeof(union art_object);
0233 art_user = kzalloc(art_len, GFP_KERNEL);
0234 if (!art_user) {
0235 ret = -ENOMEM;
0236 goto free_art;
0237 }
0238
0239 for (i = 0; i < count; i++) {
0240
0241 get_single_name(arts[i].source, art_user[i].source_device);
0242 get_single_name(arts[i].target, art_user[i].target_device);
0243
0244 BUILD_BUG_ON(sizeof(art_user[i].data) !=
0245 sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2));
0246 memcpy(&art_user[i].data, &arts[i].data, sizeof(art_user[i].data));
0247 }
0248
0249 if (copy_to_user(ubuf, art_user, art_len))
0250 ret = -EFAULT;
0251 kfree(art_user);
0252 free_art:
0253 kfree(arts);
0254 return ret;
0255 }
0256
0257 static int fill_trt(char __user *ubuf)
0258 {
0259 int i;
0260 int ret;
0261 int count;
0262 int trt_len;
0263 struct trt *trts = NULL;
0264 union trt_object *trt_user;
0265
0266 ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false);
0267 if (ret)
0268 goto free_trt;
0269 trt_len = count * sizeof(union trt_object);
0270 trt_user = kzalloc(trt_len, GFP_KERNEL);
0271 if (!trt_user) {
0272 ret = -ENOMEM;
0273 goto free_trt;
0274 }
0275
0276 for (i = 0; i < count; i++) {
0277
0278 get_single_name(trts[i].source, trt_user[i].source_device);
0279 get_single_name(trts[i].target, trt_user[i].target_device);
0280 trt_user[i].sample_period = trts[i].sample_period;
0281 trt_user[i].influence = trts[i].influence;
0282 }
0283
0284 if (copy_to_user(ubuf, trt_user, trt_len))
0285 ret = -EFAULT;
0286 kfree(trt_user);
0287 free_trt:
0288 kfree(trts);
0289 return ret;
0290 }
0291
0292 static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
0293 unsigned long __arg)
0294 {
0295 int ret = 0;
0296 unsigned long length = 0;
0297 int count = 0;
0298 char __user *arg = (void __user *)__arg;
0299 struct trt *trts = NULL;
0300 struct art *arts = NULL;
0301
0302 switch (cmd) {
0303 case ACPI_THERMAL_GET_TRT_COUNT:
0304 ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,
0305 &trts, false);
0306 kfree(trts);
0307 if (!ret)
0308 return put_user(count, (unsigned long __user *)__arg);
0309 return ret;
0310 case ACPI_THERMAL_GET_TRT_LEN:
0311 ret = acpi_parse_trt(acpi_thermal_rel_handle, &count,
0312 &trts, false);
0313 kfree(trts);
0314 length = count * sizeof(union trt_object);
0315 if (!ret)
0316 return put_user(length, (unsigned long __user *)__arg);
0317 return ret;
0318 case ACPI_THERMAL_GET_TRT:
0319 return fill_trt(arg);
0320 case ACPI_THERMAL_GET_ART_COUNT:
0321 ret = acpi_parse_art(acpi_thermal_rel_handle, &count,
0322 &arts, false);
0323 kfree(arts);
0324 if (!ret)
0325 return put_user(count, (unsigned long __user *)__arg);
0326 return ret;
0327 case ACPI_THERMAL_GET_ART_LEN:
0328 ret = acpi_parse_art(acpi_thermal_rel_handle, &count,
0329 &arts, false);
0330 kfree(arts);
0331 length = count * sizeof(union art_object);
0332 if (!ret)
0333 return put_user(length, (unsigned long __user *)__arg);
0334 return ret;
0335
0336 case ACPI_THERMAL_GET_ART:
0337 return fill_art(arg);
0338
0339 default:
0340 return -ENOTTY;
0341 }
0342 }
0343
0344 static const struct file_operations acpi_thermal_rel_fops = {
0345 .owner = THIS_MODULE,
0346 .open = acpi_thermal_rel_open,
0347 .release = acpi_thermal_rel_release,
0348 .unlocked_ioctl = acpi_thermal_rel_ioctl,
0349 .llseek = no_llseek,
0350 };
0351
0352 static struct miscdevice acpi_thermal_rel_misc_device = {
0353 .minor = MISC_DYNAMIC_MINOR,
0354 "acpi_thermal_rel",
0355 &acpi_thermal_rel_fops
0356 };
0357
0358 int acpi_thermal_rel_misc_device_add(acpi_handle handle)
0359 {
0360 acpi_thermal_rel_handle = handle;
0361
0362 return misc_register(&acpi_thermal_rel_misc_device);
0363 }
0364 EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add);
0365
0366 int acpi_thermal_rel_misc_device_remove(acpi_handle handle)
0367 {
0368 misc_deregister(&acpi_thermal_rel_misc_device);
0369
0370 return 0;
0371 }
0372 EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove);
0373
0374 MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
0375 MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com");
0376 MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver");
0377 MODULE_LICENSE("GPL v2");