host_notify_class.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. * drivers/usb/notify/host_notify_class.c
  3. *
  4. * Copyright (C) 2011 Samsung, Inc.
  5. * Author: Dongrak Shin <dongrak.shin@samsung.com>
  6. *
  7. */
  8. #include <linux/module.h>
  9. #include <linux/types.h>
  10. #include <linux/init.h>
  11. #include <linux/device.h>
  12. #include <linux/slab.h>
  13. #include <linux/fs.h>
  14. #include <linux/err.h>
  15. #include <linux/host_notify.h>
  16. struct notify_data {
  17. struct class *host_notify_class;
  18. atomic_t device_count;
  19. };
  20. static struct notify_data host_notify;
  21. static ssize_t mode_show(
  22. struct device *dev, struct device_attribute *attr, char *buf)
  23. {
  24. struct host_notify_dev *ndev = (struct host_notify_dev *)
  25. dev_get_drvdata(dev);
  26. char *mode;
  27. switch (ndev->mode) {
  28. case NOTIFY_HOST_MODE:
  29. mode = "HOST";
  30. break;
  31. case NOTIFY_PERIPHERAL_MODE:
  32. mode = "PERIPHERAL";
  33. break;
  34. case NOTIFY_TEST_MODE:
  35. mode = "TEST";
  36. break;
  37. case NOTIFY_NONE_MODE:
  38. default:
  39. mode = "NONE";
  40. break;
  41. }
  42. return snprintf(buf, sizeof(mode)+1, "%s\n", mode);
  43. }
  44. static ssize_t mode_store(
  45. struct device *dev, struct device_attribute *attr,
  46. const char *buf, size_t size)
  47. {
  48. struct host_notify_dev *ndev = (struct host_notify_dev *)
  49. dev_get_drvdata(dev);
  50. char *mode;
  51. size_t ret = -ENOMEM;
  52. mode = kzalloc(size+1, GFP_KERNEL);
  53. if (!mode)
  54. goto error;
  55. sscanf(buf, "%s", mode);
  56. if (ndev->set_mode) {
  57. if (!strncmp(mode, "HOST", 4))
  58. ndev->set_mode(NOTIFY_SET_ON);
  59. else if (!strncmp(mode, "NONE", 4))
  60. ndev->set_mode(NOTIFY_SET_OFF);
  61. printk(KERN_INFO "host_notify: set mode %s\n", mode);
  62. }
  63. ret = size;
  64. kfree(mode);
  65. error:
  66. return ret;
  67. }
  68. static ssize_t booster_show(struct device *dev, struct device_attribute *attr,
  69. char *buf)
  70. {
  71. struct host_notify_dev *ndev = (struct host_notify_dev *)
  72. dev_get_drvdata(dev);
  73. char *booster;
  74. switch (ndev->booster) {
  75. case NOTIFY_POWER_ON:
  76. booster = "ON";
  77. break;
  78. case NOTIFY_POWER_OFF:
  79. default:
  80. booster = "OFF";
  81. break;
  82. }
  83. pr_info("booster_show: %s\n", booster);
  84. return snprintf(buf, sizeof(booster)+1, "%s\n", booster);
  85. }
  86. static ssize_t booster_store(
  87. struct device *dev, struct device_attribute *attr,
  88. const char *buf, size_t size)
  89. {
  90. struct host_notify_dev *ndev = (struct host_notify_dev *)
  91. dev_get_drvdata(dev);
  92. char *booster;
  93. size_t ret = -ENOMEM;
  94. booster = kzalloc(size+1, GFP_KERNEL);
  95. if (!booster)
  96. goto error;
  97. sscanf(buf, "%s", booster);
  98. if (ndev->set_booster) {
  99. if (!strncmp(booster, "ON", 2)) {
  100. ndev->set_booster(NOTIFY_SET_ON, ndev);
  101. ndev->mode = NOTIFY_TEST_MODE;
  102. } else if (!strncmp(booster, "OFF", 3)) {
  103. ndev->set_booster(NOTIFY_SET_OFF, ndev);
  104. ndev->mode = NOTIFY_NONE_MODE;
  105. }
  106. printk(KERN_INFO "host_notify: set booster %s\n", booster);
  107. }
  108. ret = size;
  109. kfree(booster);
  110. error:
  111. return ret;
  112. }
  113. static DEVICE_ATTR(mode, 0664, mode_show, mode_store);
  114. static DEVICE_ATTR(booster, 0664, booster_show, booster_store);
  115. static struct attribute *host_notify_attrs[] = {
  116. &dev_attr_mode.attr,
  117. &dev_attr_booster.attr,
  118. NULL,
  119. };
  120. static struct attribute_group host_notify_attr_grp = {
  121. .attrs = host_notify_attrs,
  122. };
  123. char *host_state_string(int type)
  124. {
  125. switch (type) {
  126. case NOTIFY_HOST_NONE: return "none";
  127. case NOTIFY_HOST_ADD: return "add";
  128. case NOTIFY_HOST_REMOVE: return "remove";
  129. case NOTIFY_HOST_OVERCURRENT: return "overcurrent";
  130. case NOTIFY_HOST_LOWBATT: return "lowbatt";
  131. case NOTIFY_HOST_BLOCK: return "block";
  132. case NOTIFY_HOST_UNKNOWN:
  133. default: return "unknown";
  134. }
  135. }
  136. int host_state_notify(struct host_notify_dev *ndev, int state)
  137. {
  138. printk(KERN_INFO "host_notify: ndev name=%s: (%s --> %s)\n",
  139. ndev->name,
  140. host_state_string(ndev->state),
  141. host_state_string(state));
  142. if (ndev->state != state) {
  143. ndev->state = state;
  144. kobject_uevent(&ndev->dev->kobj, KOBJ_CHANGE);
  145. return 1;
  146. }
  147. return 0;
  148. }
  149. EXPORT_SYMBOL_GPL(host_state_notify);
  150. static int
  151. host_notify_uevent(struct device *dev, struct kobj_uevent_env *env)
  152. {
  153. struct host_notify_dev *ndev = (struct host_notify_dev *)
  154. dev_get_drvdata(dev);
  155. char *state;
  156. if (!ndev) {
  157. /* this happens when the device is first created */
  158. return 0;
  159. }
  160. switch (ndev->state) {
  161. case NOTIFY_HOST_ADD:
  162. state = "ADD";
  163. break;
  164. case NOTIFY_HOST_REMOVE:
  165. state = "REMOVE";
  166. break;
  167. case NOTIFY_HOST_OVERCURRENT:
  168. state = "OVERCURRENT";
  169. break;
  170. case NOTIFY_HOST_LOWBATT:
  171. state = "LOWBATT";
  172. break;
  173. case NOTIFY_HOST_BLOCK:
  174. state = "BLOCK";
  175. break;
  176. case NOTIFY_HOST_UNKNOWN:
  177. state = "UNKNOWN";
  178. break;
  179. case NOTIFY_HOST_NONE:
  180. default:
  181. return 0;
  182. }
  183. if (add_uevent_var(env, "DEVNAME=%s", ndev->dev->kobj.name))
  184. return -ENOMEM;
  185. if (add_uevent_var(env, "STATE=%s", state))
  186. return -ENOMEM;
  187. return 0;
  188. }
  189. static int create_notify_class(void)
  190. {
  191. if (!host_notify.host_notify_class) {
  192. host_notify.host_notify_class
  193. = class_create(THIS_MODULE, "host_notify");
  194. if (IS_ERR(host_notify.host_notify_class))
  195. return PTR_ERR(host_notify.host_notify_class);
  196. atomic_set(&host_notify.device_count, 0);
  197. host_notify.host_notify_class->dev_uevent = host_notify_uevent;
  198. }
  199. return 0;
  200. }
  201. int host_notify_dev_register(struct host_notify_dev *ndev)
  202. {
  203. int ret;
  204. if (!host_notify.host_notify_class) {
  205. ret = create_notify_class();
  206. if (ret < 0)
  207. return ret;
  208. }
  209. ndev->index = atomic_inc_return(&host_notify.device_count);
  210. ndev->dev = device_create(host_notify.host_notify_class, NULL,
  211. MKDEV(0, ndev->index), NULL, ndev->name);
  212. if (IS_ERR(ndev->dev))
  213. return PTR_ERR(ndev->dev);
  214. ret = sysfs_create_group(&ndev->dev->kobj, &host_notify_attr_grp);
  215. if (ret < 0) {
  216. device_destroy(host_notify.host_notify_class,
  217. MKDEV(0, ndev->index));
  218. return ret;
  219. }
  220. dev_set_drvdata(ndev->dev, ndev);
  221. ndev->state = 0;
  222. return 0;
  223. }
  224. EXPORT_SYMBOL_GPL(host_notify_dev_register);
  225. void host_notify_dev_unregister(struct host_notify_dev *ndev)
  226. {
  227. ndev->state = NOTIFY_HOST_NONE;
  228. sysfs_remove_group(&ndev->dev->kobj, &host_notify_attr_grp);
  229. device_destroy(host_notify.host_notify_class, MKDEV(0, ndev->index));
  230. dev_set_drvdata(ndev->dev, NULL);
  231. }
  232. EXPORT_SYMBOL_GPL(host_notify_dev_unregister);
  233. static int __init notify_class_init(void)
  234. {
  235. return create_notify_class();
  236. }
  237. static void __exit notify_class_exit(void)
  238. {
  239. class_destroy(host_notify.host_notify_class);
  240. }
  241. module_init(notify_class_init);
  242. module_exit(notify_class_exit);
  243. MODULE_AUTHOR("Dongrak Shin <dongrak.shin@samsung.com>");
  244. MODULE_DESCRIPTION("Usb host notify driver");
  245. MODULE_LICENSE("GPL");