tuxonice_sysfs.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * kernel/power/tuxonice_sysfs.c
  3. *
  4. * Copyright (C) 2002-2014 Nigel Cunningham (nigel at tuxonice net)
  5. *
  6. * This file is released under the GPLv2.
  7. *
  8. * This file contains support for sysfs entries for tuning TuxOnIce.
  9. *
  10. * We have a generic handler that deals with the most common cases, and
  11. * hooks for special handlers to use.
  12. */
  13. #include <linux/suspend.h>
  14. #include "tuxonice_sysfs.h"
  15. #include "tuxonice.h"
  16. #include "tuxonice_storage.h"
  17. #include "tuxonice_alloc.h"
  18. static int toi_sysfs_initialised;
  19. static void toi_initialise_sysfs(void);
  20. static struct toi_sysfs_data sysfs_params[];
  21. #define to_sysfs_data(_attr) container_of(_attr, struct toi_sysfs_data, attr)
  22. static void toi_main_wrapper(void)
  23. {
  24. toi_try_hibernate();
  25. }
  26. static ssize_t toi_attr_show(struct kobject *kobj, struct attribute *attr,
  27. char *page)
  28. {
  29. struct toi_sysfs_data *sysfs_data = to_sysfs_data(attr);
  30. int len = 0;
  31. int full_prep = sysfs_data->flags & SYSFS_NEEDS_SM_FOR_READ;
  32. if (full_prep && toi_start_anything(0))
  33. return -EBUSY;
  34. if (sysfs_data->flags & SYSFS_NEEDS_SM_FOR_READ)
  35. toi_prepare_usm();
  36. switch (sysfs_data->type) {
  37. case TOI_SYSFS_DATA_CUSTOM:
  38. len = (sysfs_data->data.special.read_sysfs) ?
  39. (sysfs_data->data.special.read_sysfs)(page, PAGE_SIZE)
  40. : 0;
  41. break;
  42. case TOI_SYSFS_DATA_BIT:
  43. len = sprintf(page, "%d\n",
  44. -test_bit(sysfs_data->data.bit.bit,
  45. sysfs_data->data.bit.bit_vector));
  46. break;
  47. case TOI_SYSFS_DATA_INTEGER:
  48. len = sprintf(page, "%d\n",
  49. *(sysfs_data->data.integer.variable));
  50. break;
  51. case TOI_SYSFS_DATA_LONG:
  52. len = sprintf(page, "%ld\n",
  53. *(sysfs_data->data.a_long.variable));
  54. break;
  55. case TOI_SYSFS_DATA_UL:
  56. len = sprintf(page, "%lu\n",
  57. *(sysfs_data->data.ul.variable));
  58. break;
  59. case TOI_SYSFS_DATA_STRING:
  60. len = sprintf(page, "%s\n",
  61. sysfs_data->data.string.variable);
  62. break;
  63. }
  64. if (sysfs_data->flags & SYSFS_NEEDS_SM_FOR_READ)
  65. toi_cleanup_usm();
  66. if (full_prep)
  67. toi_finish_anything(0);
  68. return len;
  69. }
  70. #define BOUND(_variable, _type) do { \
  71. if (*_variable < sysfs_data->data._type.minimum) \
  72. *_variable = sysfs_data->data._type.minimum; \
  73. else if (*_variable > sysfs_data->data._type.maximum) \
  74. *_variable = sysfs_data->data._type.maximum; \
  75. } while (0)
  76. static ssize_t toi_attr_store(struct kobject *kobj, struct attribute *attr,
  77. const char *my_buf, size_t count)
  78. {
  79. int assigned_temp_buffer = 0, result = count;
  80. struct toi_sysfs_data *sysfs_data = to_sysfs_data(attr);
  81. if (toi_start_anything((sysfs_data->flags & SYSFS_HIBERNATE_OR_RESUME)))
  82. return -EBUSY;
  83. ((char *) my_buf)[count] = 0;
  84. if (sysfs_data->flags & SYSFS_NEEDS_SM_FOR_WRITE)
  85. toi_prepare_usm();
  86. switch (sysfs_data->type) {
  87. case TOI_SYSFS_DATA_CUSTOM:
  88. if (sysfs_data->data.special.write_sysfs)
  89. result = (sysfs_data->data.special.write_sysfs)(my_buf,
  90. count);
  91. break;
  92. case TOI_SYSFS_DATA_BIT:
  93. {
  94. unsigned long value;
  95. result = kstrtoul(my_buf, 0, &value);
  96. if (result)
  97. break;
  98. if (value)
  99. set_bit(sysfs_data->data.bit.bit,
  100. (sysfs_data->data.bit.bit_vector));
  101. else
  102. clear_bit(sysfs_data->data.bit.bit,
  103. (sysfs_data->data.bit.bit_vector));
  104. }
  105. break;
  106. case TOI_SYSFS_DATA_INTEGER:
  107. {
  108. long temp;
  109. result = kstrtol(my_buf, 0, &temp);
  110. if (result)
  111. break;
  112. *(sysfs_data->data.integer.variable) = (int) temp;
  113. BOUND(sysfs_data->data.integer.variable, integer);
  114. break;
  115. }
  116. case TOI_SYSFS_DATA_LONG:
  117. {
  118. long *variable = sysfs_data->data.a_long.variable;
  119. result = kstrtol(my_buf, 0, variable);
  120. if (result)
  121. break;
  122. BOUND(variable, a_long);
  123. break;
  124. }
  125. case TOI_SYSFS_DATA_UL:
  126. {
  127. unsigned long *variable =
  128. sysfs_data->data.ul.variable;
  129. result = kstrtoul(my_buf, 0, variable);
  130. if (result)
  131. break;
  132. BOUND(variable, ul);
  133. break;
  134. }
  135. break;
  136. case TOI_SYSFS_DATA_STRING:
  137. {
  138. int copy_len = count;
  139. char *variable =
  140. sysfs_data->data.string.variable;
  141. if (sysfs_data->data.string.max_length &&
  142. (copy_len > sysfs_data->data.string.max_length))
  143. copy_len = sysfs_data->data.string.max_length;
  144. if (!variable) {
  145. variable = (char *) toi_get_zeroed_page(31,
  146. TOI_ATOMIC_GFP);
  147. sysfs_data->data.string.variable = variable;
  148. assigned_temp_buffer = 1;
  149. }
  150. strncpy(variable, my_buf, copy_len);
  151. if (copy_len && my_buf[copy_len - 1] == '\n')
  152. variable[count - 1] = 0;
  153. variable[count] = 0;
  154. }
  155. break;
  156. }
  157. if (!result)
  158. result = count;
  159. /* Side effect routine? */
  160. if (result == count && sysfs_data->write_side_effect)
  161. sysfs_data->write_side_effect();
  162. /* Free temporary buffers */
  163. if (assigned_temp_buffer) {
  164. toi_free_page(31,
  165. (unsigned long) sysfs_data->data.string.variable);
  166. sysfs_data->data.string.variable = NULL;
  167. }
  168. if (sysfs_data->flags & SYSFS_NEEDS_SM_FOR_WRITE)
  169. toi_cleanup_usm();
  170. toi_finish_anything(sysfs_data->flags & SYSFS_HIBERNATE_OR_RESUME);
  171. return result;
  172. }
  173. static const struct sysfs_ops toi_sysfs_ops = {
  174. .show = &toi_attr_show,
  175. .store = &toi_attr_store,
  176. };
  177. static struct kobj_type toi_ktype = {
  178. .sysfs_ops = &toi_sysfs_ops,
  179. };
  180. struct kobject *tuxonice_kobj;
  181. /* Non-module sysfs entries.
  182. *
  183. * This array contains entries that are automatically registered at
  184. * boot. Modules and the console code register their own entries separately.
  185. */
  186. static struct toi_sysfs_data sysfs_params[] = {
  187. SYSFS_CUSTOM("do_hibernate", SYSFS_WRITEONLY, NULL, NULL,
  188. SYSFS_HIBERNATING, toi_main_wrapper),
  189. SYSFS_CUSTOM("do_resume", SYSFS_WRITEONLY, NULL, NULL,
  190. SYSFS_RESUMING, toi_try_resume)
  191. };
  192. void remove_toi_sysdir(struct kobject *kobj)
  193. {
  194. if (!kobj)
  195. return;
  196. kobject_put(kobj);
  197. }
  198. struct kobject *make_toi_sysdir(char *name)
  199. {
  200. struct kobject *kobj = kobject_create_and_add(name, tuxonice_kobj);
  201. if (!kobj) {
  202. pr_warn("TuxOnIce: Can't allocate kobject for sysfs dir!\n");
  203. return NULL;
  204. }
  205. kobj->ktype = &toi_ktype;
  206. return kobj;
  207. }
  208. /* toi_register_sysfs_file
  209. *
  210. * Helper for registering a new /sysfs/tuxonice entry.
  211. */
  212. int toi_register_sysfs_file(
  213. struct kobject *kobj,
  214. struct toi_sysfs_data *toi_sysfs_data)
  215. {
  216. int result;
  217. if (!toi_sysfs_initialised)
  218. toi_initialise_sysfs();
  219. result = sysfs_create_file(kobj, &toi_sysfs_data->attr);
  220. if (result)
  221. pr_warn("TuxOnIce: sysfs_create_file for %s returned %d.\n",
  222. toi_sysfs_data->attr.name, result);
  223. kobj->ktype = &toi_ktype;
  224. return result;
  225. }
  226. EXPORT_SYMBOL_GPL(toi_register_sysfs_file);
  227. /* toi_unregister_sysfs_file
  228. *
  229. * Helper for removing unwanted /sys/power/tuxonice entries.
  230. *
  231. */
  232. void toi_unregister_sysfs_file(struct kobject *kobj,
  233. struct toi_sysfs_data *toi_sysfs_data)
  234. {
  235. sysfs_remove_file(kobj, &toi_sysfs_data->attr);
  236. }
  237. EXPORT_SYMBOL_GPL(toi_unregister_sysfs_file);
  238. void toi_cleanup_sysfs(void)
  239. {
  240. int i,
  241. numfiles = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data);
  242. if (!toi_sysfs_initialised)
  243. return;
  244. for (i = 0; i < numfiles; i++)
  245. toi_unregister_sysfs_file(tuxonice_kobj, &sysfs_params[i]);
  246. kobject_put(tuxonice_kobj);
  247. toi_sysfs_initialised = 0;
  248. }
  249. /* toi_initialise_sysfs
  250. *
  251. * Initialise the /sysfs/tuxonice directory.
  252. */
  253. static void toi_initialise_sysfs(void)
  254. {
  255. int i;
  256. int numfiles = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data);
  257. if (toi_sysfs_initialised)
  258. return;
  259. /* Make our TuxOnIce directory a child of /sys/power */
  260. tuxonice_kobj = kobject_create_and_add("tuxonice", power_kobj);
  261. if (!tuxonice_kobj)
  262. return;
  263. toi_sysfs_initialised = 1;
  264. for (i = 0; i < numfiles; i++)
  265. toi_register_sysfs_file(tuxonice_kobj, &sysfs_params[i]);
  266. }
  267. int toi_sysfs_init(void)
  268. {
  269. toi_initialise_sysfs();
  270. return 0;
  271. }
  272. void toi_sysfs_exit(void)
  273. {
  274. toi_cleanup_sysfs();
  275. }