msm_thermal-dev.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /* Copyright (c) 2013, The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. */
  13. #include <linux/kernel.h>
  14. #include <linux/fs.h>
  15. #include <linux/types.h>
  16. #include <linux/device.h>
  17. #include <linux/slab.h>
  18. #include <linux/msm_thermal_ioctl.h>
  19. #include <linux/msm_thermal.h>
  20. #include <linux/uaccess.h>
  21. #include <linux/cdev.h>
  22. #include <linux/semaphore.h>
  23. #include <linux/module.h>
  24. struct msm_thermal_ioctl_dev {
  25. struct semaphore sem;
  26. struct cdev char_dev;
  27. };
  28. static int msm_thermal_major;
  29. static struct class *thermal_class;
  30. static struct msm_thermal_ioctl_dev *msm_thermal_dev;
  31. static int msm_thermal_ioctl_open(struct inode *node, struct file *filep)
  32. {
  33. int ret = 0;
  34. struct msm_thermal_ioctl_dev *dev;
  35. dev = container_of(node->i_cdev, struct msm_thermal_ioctl_dev,
  36. char_dev);
  37. filep->private_data = dev;
  38. return ret;
  39. }
  40. static int msm_thermal_ioctl_release(struct inode *node, struct file *filep)
  41. {
  42. pr_debug("%s: IOCTL: release\n", KBUILD_MODNAME);
  43. return 0;
  44. }
  45. static long validate_and_copy(unsigned int *cmd, unsigned long *arg,
  46. struct msm_thermal_ioctl *query)
  47. {
  48. long ret = 0, err_val = 0;
  49. if ((_IOC_TYPE(*cmd) != MSM_THERMAL_MAGIC_NUM) ||
  50. (_IOC_NR(*cmd) >= MSM_CMD_MAX_NR)) {
  51. ret = -ENOTTY;
  52. goto validate_exit;
  53. }
  54. if (_IOC_DIR(*cmd) & _IOC_READ) {
  55. err_val = !access_ok(VERIFY_WRITE, (void __user *)*arg,
  56. _IOC_SIZE(*cmd));
  57. } else if (_IOC_DIR(*cmd) & _IOC_WRITE) {
  58. err_val = !access_ok(VERIFY_READ, (void __user *)*arg,
  59. _IOC_SIZE(*cmd));
  60. }
  61. if (err_val) {
  62. ret = -EFAULT;
  63. goto validate_exit;
  64. }
  65. if (copy_from_user(query, (void __user *)(*arg),
  66. sizeof(struct msm_thermal_ioctl))) {
  67. ret = -EACCES;
  68. goto validate_exit;
  69. }
  70. if (query->size != sizeof(struct msm_thermal_ioctl)) {
  71. pr_err("%s: Invalid input argument size\n", __func__);
  72. ret = -EINVAL;
  73. goto validate_exit;
  74. }
  75. switch (*cmd) {
  76. case MSM_THERMAL_SET_CPU_MAX_FREQUENCY:
  77. case MSM_THERMAL_SET_CPU_MIN_FREQUENCY:
  78. if (query->cpu_freq.cpu_num >= num_possible_cpus()) {
  79. pr_err("%s: Invalid CPU number: %u\n", __func__,
  80. query->cpu_freq.cpu_num);
  81. ret = -EINVAL;
  82. goto validate_exit;
  83. }
  84. break;
  85. default:
  86. ret = -ENOTTY;
  87. goto validate_exit;
  88. break;
  89. }
  90. validate_exit:
  91. return ret;
  92. }
  93. static long msm_thermal_ioctl_process(struct file *filep, unsigned int cmd,
  94. unsigned long arg)
  95. {
  96. long ret = 0;
  97. struct msm_thermal_ioctl query;
  98. pr_debug("%s: IOCTL: processing cmd:%u\n", KBUILD_MODNAME, cmd);
  99. ret = validate_and_copy(&cmd, &arg, &query);
  100. if (ret)
  101. goto process_exit;
  102. switch (cmd) {
  103. case MSM_THERMAL_SET_CPU_MAX_FREQUENCY:
  104. ret = msm_thermal_set_frequency(query.cpu_freq.cpu_num,
  105. query.cpu_freq.freq_req, true);
  106. break;
  107. case MSM_THERMAL_SET_CPU_MIN_FREQUENCY:
  108. ret = msm_thermal_set_frequency(query.cpu_freq.cpu_num,
  109. query.cpu_freq.freq_req, false);
  110. break;
  111. default:
  112. ret = -ENOTTY;
  113. goto process_exit;
  114. }
  115. process_exit:
  116. return ret;
  117. }
  118. static const struct file_operations msm_thermal_fops = {
  119. .owner = THIS_MODULE,
  120. .open = msm_thermal_ioctl_open,
  121. .unlocked_ioctl = msm_thermal_ioctl_process,
  122. .release = msm_thermal_ioctl_release,
  123. };
  124. int msm_thermal_ioctl_init()
  125. {
  126. int ret = 0;
  127. dev_t thermal_dev;
  128. struct device *therm_device;
  129. ret = alloc_chrdev_region(&thermal_dev, 0, 1,
  130. MSM_THERMAL_IOCTL_NAME);
  131. if (ret < 0) {
  132. pr_err("%s: Error in allocating char device region. Err:%d\n",
  133. KBUILD_MODNAME, ret);
  134. goto ioctl_init_exit;
  135. }
  136. msm_thermal_major = MAJOR(thermal_dev);
  137. thermal_class = class_create(THIS_MODULE, "msm_thermal");
  138. if (IS_ERR(thermal_class)) {
  139. pr_err("%s: Error in creating class\n",
  140. KBUILD_MODNAME);
  141. ret = PTR_ERR(thermal_class);
  142. goto ioctl_class_fail;
  143. }
  144. therm_device = device_create(thermal_class, NULL, thermal_dev, NULL,
  145. MSM_THERMAL_IOCTL_NAME);
  146. if (IS_ERR(therm_device)) {
  147. pr_err("%s: Error in creating character device\n",
  148. KBUILD_MODNAME);
  149. ret = PTR_ERR(therm_device);
  150. goto ioctl_dev_fail;
  151. }
  152. msm_thermal_dev = kmalloc(sizeof(struct msm_thermal_ioctl_dev),
  153. GFP_KERNEL);
  154. if (!msm_thermal_dev) {
  155. pr_err("%s: Error allocating memory\n",
  156. KBUILD_MODNAME);
  157. ret = -ENOMEM;
  158. goto ioctl_clean_all;
  159. }
  160. memset(msm_thermal_dev, 0, sizeof(struct msm_thermal_ioctl_dev));
  161. sema_init(&msm_thermal_dev->sem, 1);
  162. cdev_init(&msm_thermal_dev->char_dev, &msm_thermal_fops);
  163. ret = cdev_add(&msm_thermal_dev->char_dev, thermal_dev, 1);
  164. if (ret < 0) {
  165. pr_err("%s: Error in adding character device\n",
  166. KBUILD_MODNAME);
  167. goto ioctl_clean_all;
  168. }
  169. return ret;
  170. ioctl_clean_all:
  171. device_destroy(thermal_class, thermal_dev);
  172. ioctl_dev_fail:
  173. class_destroy(thermal_class);
  174. ioctl_class_fail:
  175. unregister_chrdev_region(thermal_dev, 1);
  176. ioctl_init_exit:
  177. return ret;
  178. }
  179. void msm_thermal_ioctl_cleanup()
  180. {
  181. dev_t thermal_dev = MKDEV(msm_thermal_major, 0);
  182. if (!msm_thermal_dev) {
  183. pr_err("%s: Thermal IOCTL cleanup already done\n",
  184. KBUILD_MODNAME);
  185. return;
  186. }
  187. device_destroy(thermal_class, thermal_dev);
  188. class_destroy(thermal_class);
  189. cdev_del(&msm_thermal_dev->char_dev);
  190. unregister_chrdev_region(thermal_dev, 1);
  191. kfree(msm_thermal_dev);
  192. msm_thermal_dev = NULL;
  193. thermal_class = NULL;
  194. }