usb_notify_sysfs.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*
  2. * drivers/usb/notify/usb_notify_sysfs.c
  3. *
  4. * Copyright (C) 2015 Samsung, Inc.
  5. * Author: Dongrak Shin <dongrak.shin@samsung.com>
  6. *
  7. */
  8. #define pr_fmt(fmt) "usb_notify: " fmt
  9. #include <linux/module.h>
  10. #include <linux/types.h>
  11. #include <linux/init.h>
  12. #include <linux/device.h>
  13. #include <linux/slab.h>
  14. #include <linux/fs.h>
  15. #include <linux/err.h>
  16. #include <linux/usb_notify_sysfs.h>
  17. struct notify_data {
  18. struct class *usb_notify_class;
  19. atomic_t device_count;
  20. };
  21. static struct notify_data usb_notify_data;
  22. static int is_valid_cmd(char *cur_cmd, char *prev_cmd)
  23. {
  24. pr_info("%s : current state=%s, previous state=%s\n",
  25. __func__, cur_cmd, prev_cmd);
  26. if (!strcmp(cur_cmd, "ON") ||
  27. !strncmp(cur_cmd, "ON_ALL_", 7)) {
  28. if (!strcmp(prev_cmd, "ON") ||
  29. !strncmp(prev_cmd, "ON_ALL_", 7)) {
  30. goto ignore;
  31. } else if (!strncmp(prev_cmd, "ON_HOST_", 8)) {
  32. goto all;
  33. } else if (!strncmp(prev_cmd, "ON_CLIENT_", 10)) {
  34. goto all;
  35. } else if (!strcmp(prev_cmd, "OFF")) {
  36. goto all;
  37. } else {
  38. goto invalid;
  39. }
  40. } else if (!strcmp(cur_cmd, "OFF")) {
  41. if (!strcmp(prev_cmd, "ON") ||
  42. !strncmp(prev_cmd, "ON_ALL_", 7)) {
  43. goto off;
  44. } else if (!strncmp(prev_cmd, "ON_HOST_", 8)) {
  45. goto off;
  46. } else if (!strncmp(prev_cmd, "ON_CLIENT_", 10)) {
  47. goto off;
  48. } else if (!strcmp(prev_cmd, "OFF")) {
  49. goto ignore;
  50. } else {
  51. goto invalid;
  52. }
  53. } else if (!strncmp(cur_cmd, "ON_HOST_", 8)) {
  54. if (!strcmp(prev_cmd, "ON") ||
  55. !strncmp(prev_cmd, "ON_ALL_", 7)) {
  56. goto host;
  57. } else if (!strncmp(prev_cmd, "ON_HOST_", 8)) {
  58. goto ignore;
  59. } else if (!strncmp(prev_cmd, "ON_CLIENT_", 10)) {
  60. goto host;
  61. } else if (!strcmp(prev_cmd, "OFF")) {
  62. goto host;
  63. } else {
  64. goto invalid;
  65. }
  66. } else if (!strncmp(cur_cmd, "ON_CLIENT_", 10)) {
  67. if (!strcmp(prev_cmd, "ON") ||
  68. !strncmp(prev_cmd, "ON_ALL_", 7)) {
  69. goto client;
  70. } else if (!strncmp(prev_cmd, "ON_HOST_", 8)) {
  71. goto client;
  72. } else if (!strncmp(prev_cmd, "ON_CLIENT_", 10)) {
  73. goto ignore;
  74. } else if (!strcmp(prev_cmd, "OFF")) {
  75. goto client;
  76. } else {
  77. goto invalid;
  78. }
  79. } else {
  80. goto invalid;
  81. }
  82. host:
  83. pr_err("%s cmd=%s is accepted.\n", __func__, cur_cmd);
  84. return NOTIFY_BLOCK_TYPE_HOST;
  85. client:
  86. pr_err("%s cmd=%s is accepted.\n", __func__, cur_cmd);
  87. return NOTIFY_BLOCK_TYPE_CLIENT;
  88. all:
  89. pr_err("%s cmd=%s is accepted.\n", __func__, cur_cmd);
  90. return NOTIFY_BLOCK_TYPE_ALL;
  91. off:
  92. pr_err("%s cmd=%s is accepted.\n", __func__, cur_cmd);
  93. return NOTIFY_BLOCK_TYPE_NONE;
  94. ignore:
  95. pr_err("%s cmd=%s is ignored but saved.\n", __func__, cur_cmd);
  96. return -EEXIST;
  97. invalid:
  98. pr_err("%s cmd=%s is invalid.\n", __func__, cur_cmd);
  99. return -EINVAL;
  100. }
  101. static ssize_t disable_show(struct device *dev, struct device_attribute *attr,
  102. char *buf)
  103. {
  104. struct usb_notify_dev *udev = (struct usb_notify_dev *)
  105. dev_get_drvdata(dev);
  106. pr_info("read disable_state %s\n", udev->disable_state_cmd);
  107. return sprintf(buf, "%s\n", udev->disable_state_cmd);
  108. }
  109. static ssize_t disable_store(
  110. struct device *dev, struct device_attribute *attr,
  111. const char *buf, size_t size)
  112. {
  113. struct usb_notify_dev *udev = (struct usb_notify_dev *)
  114. dev_get_drvdata(dev);
  115. char *disable;
  116. int size_ret, param = -EINVAL;
  117. size_t ret = -ENOMEM;
  118. if (size > MAX_DISABLE_STR_LEN) {
  119. pr_err("%s size(%zu) is too long.\n", __func__, size);
  120. goto error;
  121. }
  122. disable = kzalloc(size+1, GFP_KERNEL);
  123. if (!disable)
  124. goto error;
  125. size_ret = sscanf(buf, "%s", disable);
  126. if (udev->set_disable) {
  127. param = is_valid_cmd(disable, udev->disable_state_cmd);
  128. if (param == -EINVAL) {
  129. ret = param;
  130. } else {
  131. if (param != -EEXIST)
  132. udev->set_disable(udev, param);
  133. memset(udev->disable_state_cmd, 0,
  134. sizeof(udev->disable_state_cmd));
  135. strncpy(udev->disable_state_cmd,
  136. disable, strlen(disable));
  137. ret = size;
  138. }
  139. } else
  140. pr_err("set_disable func is NULL\n");
  141. kfree(disable);
  142. error:
  143. return ret;
  144. }
  145. static DEVICE_ATTR(disable, 0664, disable_show, disable_store);
  146. static struct attribute *usb_notify_attrs[] = {
  147. &dev_attr_disable.attr,
  148. NULL,
  149. };
  150. static struct attribute_group usb_notify_attr_grp = {
  151. .attrs = usb_notify_attrs,
  152. };
  153. static int create_usb_notify_class(void)
  154. {
  155. if (!usb_notify_data.usb_notify_class) {
  156. usb_notify_data.usb_notify_class
  157. = class_create(THIS_MODULE, "usb_notify");
  158. if (IS_ERR(usb_notify_data.usb_notify_class))
  159. return PTR_ERR(usb_notify_data.usb_notify_class);
  160. atomic_set(&usb_notify_data.device_count, 0);
  161. }
  162. return 0;
  163. }
  164. int usb_notify_dev_register(struct usb_notify_dev *udev)
  165. {
  166. int ret;
  167. if (!usb_notify_data.usb_notify_class) {
  168. ret = create_usb_notify_class();
  169. if (ret < 0)
  170. return ret;
  171. }
  172. udev->index = atomic_inc_return(&usb_notify_data.device_count);
  173. udev->dev = device_create(usb_notify_data.usb_notify_class, NULL,
  174. MKDEV(0, udev->index), NULL, udev->name);
  175. if (IS_ERR(udev->dev))
  176. return PTR_ERR(udev->dev);
  177. udev->disable_state = 0;
  178. strncpy(udev->disable_state_cmd, "OFF", sizeof(udev->disable_state_cmd)-1);
  179. ret = sysfs_create_group(&udev->dev->kobj, &usb_notify_attr_grp);
  180. if (ret < 0) {
  181. device_destroy(usb_notify_data.usb_notify_class,
  182. MKDEV(0, udev->index));
  183. return ret;
  184. }
  185. dev_set_drvdata(udev->dev, udev);
  186. return 0;
  187. }
  188. EXPORT_SYMBOL_GPL(usb_notify_dev_register);
  189. void usb_notify_dev_unregister(struct usb_notify_dev *udev)
  190. {
  191. sysfs_remove_group(&udev->dev->kobj, &usb_notify_attr_grp);
  192. device_destroy(usb_notify_data.usb_notify_class, MKDEV(0, udev->index));
  193. dev_set_drvdata(udev->dev, NULL);
  194. }
  195. EXPORT_SYMBOL_GPL(usb_notify_dev_unregister);
  196. int usb_notify_class_init(void)
  197. {
  198. return create_usb_notify_class();
  199. }
  200. EXPORT_SYMBOL_GPL(usb_notify_class_init);
  201. void usb_notify_class_exit(void)
  202. {
  203. class_destroy(usb_notify_data.usb_notify_class);
  204. }
  205. EXPORT_SYMBOL_GPL(usb_notify_class_exit);