gh_guest_pops.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2021, The Linux Foundation. All rights reserved.
  4. */
  5. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  6. #include <linux/module.h>
  7. #include <linux/kernel.h>
  8. #include <linux/delay.h>
  9. #include <linux/input.h>
  10. #include <linux/notifier.h>
  11. #include <linux/gunyah/gh_rm_drv.h>
  12. #define GH_GUEST_POPS_POFF_BUTTON_HOLD_SHUTDOWN_DELAY_MS 1000
  13. #define GH_GUEST_POPS_POFF_BUTTON_HOLD_RESTART_DELAY_MS 500
  14. static struct input_dev *gh_vm_poff_input;
  15. static struct notifier_block rm_nb;
  16. static struct kobj_type gh_guest_kobj_type = {
  17. .sysfs_ops = &kobj_sysfs_ops,
  18. };
  19. static struct kobject gh_guest_kobj;
  20. static int gh_guest_pops_handle_stop_shutdown(u32 stop_reason)
  21. {
  22. /* Emulate a KEY_POWER event to notify user-space of a shutdown */
  23. pr_info("Sending KEY_POWER event\n");
  24. input_report_key(gh_vm_poff_input, KEY_POWER, 1);
  25. input_sync(gh_vm_poff_input);
  26. switch (stop_reason) {
  27. case GH_VM_STOP_SHUTDOWN:
  28. msleep(GH_GUEST_POPS_POFF_BUTTON_HOLD_SHUTDOWN_DELAY_MS);
  29. break;
  30. case GH_VM_STOP_RESTART:
  31. msleep(GH_GUEST_POPS_POFF_BUTTON_HOLD_RESTART_DELAY_MS);
  32. break;
  33. }
  34. input_report_key(gh_vm_poff_input, KEY_POWER, 0);
  35. input_sync(gh_vm_poff_input);
  36. return 0;
  37. }
  38. static int gh_guest_pops_handle_stop_crash(void)
  39. {
  40. panic("Panic requested by Primary-VM\n");
  41. return 0;
  42. }
  43. static int
  44. gh_guest_pops_vm_shutdown(struct gh_rm_notif_vm_shutdown_payload *vm_shutdown)
  45. {
  46. switch (vm_shutdown->stop_reason) {
  47. case GH_VM_STOP_SHUTDOWN:
  48. return gh_guest_pops_handle_stop_shutdown(vm_shutdown->stop_reason);
  49. case GH_VM_STOP_CRASH:
  50. return gh_guest_pops_handle_stop_crash();
  51. case GH_VM_STOP_RESTART:
  52. return gh_guest_pops_handle_stop_shutdown(vm_shutdown->stop_reason);
  53. }
  54. return 0;
  55. }
  56. static int gh_guest_pops_rm_notifer_fn(struct notifier_block *nb,
  57. unsigned long cmd, void *data)
  58. {
  59. switch (cmd) {
  60. case GH_RM_NOTIF_VM_SHUTDOWN:
  61. return gh_guest_pops_vm_shutdown(data);
  62. }
  63. return NOTIFY_DONE;
  64. }
  65. static int __init gh_guest_pops_init_poff(void)
  66. {
  67. int ret;
  68. gh_vm_poff_input = input_allocate_device();
  69. if (!gh_vm_poff_input)
  70. return -ENOMEM;
  71. input_set_capability(gh_vm_poff_input, EV_KEY, KEY_POWER);
  72. ret = input_register_device(gh_vm_poff_input);
  73. if (ret)
  74. goto fail_register;
  75. rm_nb.notifier_call = gh_guest_pops_rm_notifer_fn;
  76. ret = gh_rm_register_notifier(&rm_nb);
  77. if (ret)
  78. goto fail_init;
  79. return 0;
  80. fail_init:
  81. input_unregister_device(gh_vm_poff_input);
  82. fail_register:
  83. input_free_device(gh_vm_poff_input);
  84. return ret;
  85. }
  86. static ssize_t gh_guest_set_app_status(struct kobject *kobj,
  87. struct kobj_attribute *attr,
  88. const char *buf,
  89. size_t count)
  90. {
  91. int ret = 0;
  92. u16 app_status;
  93. ret = kstrtou16(buf, 0, &app_status);
  94. if (ret)
  95. return -EINVAL;
  96. if (app_status != GH_RM_APP_STATUS_TUI_SERVICE_BOOT)
  97. return -EINVAL;
  98. ret = gh_rm_vm_set_app_status(app_status);
  99. if (ret) {
  100. pr_err("Failed to set the APP status\n");
  101. return -EINVAL;
  102. }
  103. return count;
  104. }
  105. static struct kobj_attribute gh_guest_attribute =
  106. __ATTR(app_status, 0220, NULL, gh_guest_set_app_status);
  107. static struct attribute *attrs[] = {
  108. &gh_guest_attribute.attr,
  109. NULL,
  110. };
  111. static struct attribute_group attr_group = {
  112. .attrs = attrs,
  113. };
  114. static int gh_guest_sysfs_init(void)
  115. {
  116. int ret = 0;
  117. ret = kobject_init_and_add(&gh_guest_kobj, &gh_guest_kobj_type,
  118. kernel_kobj, "%s", "vm_set_status");
  119. if (ret) {
  120. pr_err("sysfs create and add failed\n");
  121. ret = -ENOMEM;
  122. goto error_return;
  123. }
  124. ret = sysfs_create_group(&gh_guest_kobj, &attr_group);
  125. if (ret) {
  126. pr_err("sysfs create group failed %d\n", ret);
  127. goto error_return;
  128. }
  129. return 0;
  130. error_return:
  131. if (kobject_name(&gh_guest_kobj) != NULL) {
  132. kobject_del(&gh_guest_kobj);
  133. kobject_put(&gh_guest_kobj);
  134. memset(&gh_guest_kobj, 0, sizeof(gh_guest_kobj));
  135. }
  136. return ret;
  137. }
  138. static void gh_guest_sysfs_cleanup(void)
  139. {
  140. sysfs_remove_group(&gh_guest_kobj, &attr_group);
  141. kobject_del(&gh_guest_kobj);
  142. kobject_put(&gh_guest_kobj);
  143. memset(&gh_guest_kobj, 0, sizeof(gh_guest_kobj));
  144. }
  145. static void gh_guest_pops_exit_poff(void)
  146. {
  147. gh_rm_unregister_notifier(&rm_nb);
  148. input_unregister_device(gh_vm_poff_input);
  149. input_free_device(gh_vm_poff_input);
  150. }
  151. static int __init gh_guest_pops_init(void)
  152. {
  153. int ret;
  154. ret = gh_guest_pops_init_poff();
  155. if (ret) {
  156. pr_err("Failed to initialize guest poweroff driver\n");
  157. return 0;
  158. }
  159. ret = gh_guest_sysfs_init();
  160. if (ret) {
  161. pr_err("Failed to init sysfs of guest_pops\n");
  162. gh_guest_pops_exit_poff();
  163. return 0;
  164. }
  165. ret = gh_rm_vm_set_os_status(GH_RM_OS_STATUS_BOOT);
  166. if (ret) {
  167. pr_err("Failed to set the OS status\n");
  168. gh_guest_pops_exit_poff();
  169. gh_guest_sysfs_cleanup();
  170. }
  171. return 0;
  172. }
  173. module_init(gh_guest_pops_init);
  174. static void __exit gh_guest_pops_exit(void)
  175. {
  176. gh_guest_pops_exit_poff();
  177. gh_guest_sysfs_cleanup();
  178. }
  179. module_exit(gh_guest_pops_exit);
  180. MODULE_LICENSE("GPL");