extcon_class.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*
  2. * drivers/extcon/extcon_class.c
  3. *
  4. * External connector (extcon) class driver
  5. *
  6. * Copyright (C) 2012 Samsung Electronics
  7. * Author: Donggeun Kim <dg77.kim@samsung.com>
  8. * Author: MyungJoo Ham <myungjoo.ham@samsung.com>
  9. *
  10. * based on android/drivers/switch/switch_class.c
  11. * Copyright (C) 2008 Google, Inc.
  12. * Author: Mike Lockwood <lockwood@android.com>
  13. *
  14. * This software is licensed under the terms of the GNU General Public
  15. * License version 2, as published by the Free Software Foundation, and
  16. * may be copied, distributed, and modified under those terms.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. */
  24. #include <linux/module.h>
  25. #include <linux/types.h>
  26. #include <linux/init.h>
  27. #include <linux/device.h>
  28. #include <linux/fs.h>
  29. #include <linux/err.h>
  30. #include <linux/extcon.h>
  31. #include <linux/slab.h>
  32. struct class *extcon_class;
  33. #if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
  34. static struct class_compat *switch_class;
  35. #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
  36. static LIST_HEAD(extcon_dev_list);
  37. static DEFINE_MUTEX(extcon_dev_list_lock);
  38. static ssize_t state_show(struct device *dev, struct device_attribute *attr,
  39. char *buf)
  40. {
  41. struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
  42. if (edev->print_state) {
  43. int ret = edev->print_state(edev, buf);
  44. if (ret >= 0)
  45. return ret;
  46. /* Use default if failed */
  47. }
  48. return sprintf(buf, "%u\n", edev->state);
  49. }
  50. static ssize_t name_show(struct device *dev, struct device_attribute *attr,
  51. char *buf)
  52. {
  53. struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
  54. /* Optional callback given by the user */
  55. if (edev->print_name) {
  56. int ret = edev->print_name(edev, buf);
  57. if (ret >= 0)
  58. return ret;
  59. }
  60. return sprintf(buf, "%s\n", dev_name(edev->dev));
  61. }
  62. /**
  63. * extcon_set_state() - Set the cable attach states of the extcon device.
  64. * @edev: the extcon device
  65. * @state: new cable attach status for @edev
  66. *
  67. * Changing the state sends uevent with environment variable containing
  68. * the name of extcon device (envp[0]) and the state output (envp[1]).
  69. * Tizen uses this format for extcon device to get events from ports.
  70. * Android uses this format as well.
  71. *
  72. * Note that notifier provides the which bits are changes in the state
  73. * variable with "val" to the callback.
  74. */
  75. void extcon_set_state(struct extcon_dev *edev, u32 state)
  76. {
  77. char name_buf[120];
  78. char state_buf[120];
  79. char *prop_buf;
  80. char *envp[3];
  81. int env_offset = 0;
  82. int length;
  83. u32 old_state = edev->state;
  84. if (edev->state != state) {
  85. edev->state = state;
  86. raw_notifier_call_chain(&edev->nh, old_state ^ edev->state,
  87. edev);
  88. prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
  89. if (prop_buf) {
  90. length = name_show(edev->dev, NULL, prop_buf);
  91. if (length > 0) {
  92. if (prop_buf[length - 1] == '\n')
  93. prop_buf[length - 1] = 0;
  94. snprintf(name_buf, sizeof(name_buf),
  95. "NAME=%s", prop_buf);
  96. envp[env_offset++] = name_buf;
  97. }
  98. length = state_show(edev->dev, NULL, prop_buf);
  99. if (length > 0) {
  100. if (prop_buf[length - 1] == '\n')
  101. prop_buf[length - 1] = 0;
  102. snprintf(state_buf, sizeof(state_buf),
  103. "STATE=%s", prop_buf);
  104. envp[env_offset++] = state_buf;
  105. }
  106. envp[env_offset] = NULL;
  107. kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp);
  108. free_page((unsigned long)prop_buf);
  109. } else {
  110. dev_err(edev->dev, "out of memory in extcon_set_state\n");
  111. kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE);
  112. }
  113. }
  114. }
  115. EXPORT_SYMBOL_GPL(extcon_set_state);
  116. /**
  117. * extcon_get_extcon_dev() - Get the extcon device instance from the name
  118. * @extcon_name: The extcon name provided with extcon_dev_register()
  119. */
  120. struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name)
  121. {
  122. struct extcon_dev *sd;
  123. mutex_lock(&extcon_dev_list_lock);
  124. list_for_each_entry(sd, &extcon_dev_list, entry) {
  125. if (!strcmp(sd->name, extcon_name))
  126. goto out;
  127. }
  128. sd = NULL;
  129. out:
  130. mutex_unlock(&extcon_dev_list_lock);
  131. return sd;
  132. }
  133. EXPORT_SYMBOL_GPL(extcon_get_extcon_dev);
  134. /**
  135. * extcon_register_notifier() - Register a notifee to get notified by
  136. * any attach status changes from the extcon.
  137. * @edev: the extcon device.
  138. * @nb: a notifier block to be registered.
  139. */
  140. int extcon_register_notifier(struct extcon_dev *edev,
  141. struct notifier_block *nb)
  142. {
  143. return raw_notifier_chain_register(&edev->nh, nb);
  144. }
  145. EXPORT_SYMBOL_GPL(extcon_register_notifier);
  146. /**
  147. * extcon_unregister_notifier() - Unregister a notifee from the extcon device.
  148. * @edev: the extcon device.
  149. * @nb: a registered notifier block to be unregistered.
  150. */
  151. int extcon_unregister_notifier(struct extcon_dev *edev,
  152. struct notifier_block *nb)
  153. {
  154. return raw_notifier_chain_unregister(&edev->nh, nb);
  155. }
  156. EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
  157. static struct device_attribute extcon_attrs[] = {
  158. __ATTR_RO(state),
  159. __ATTR_RO(name),
  160. };
  161. static int create_extcon_class(void)
  162. {
  163. if (!extcon_class) {
  164. extcon_class = class_create(THIS_MODULE, "extcon");
  165. if (IS_ERR(extcon_class))
  166. return PTR_ERR(extcon_class);
  167. extcon_class->dev_attrs = extcon_attrs;
  168. #if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
  169. switch_class = class_compat_register("switch");
  170. if (WARN(!switch_class, "cannot allocate"))
  171. return -ENOMEM;
  172. #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
  173. }
  174. return 0;
  175. }
  176. static void extcon_cleanup(struct extcon_dev *edev, bool skip)
  177. {
  178. mutex_lock(&extcon_dev_list_lock);
  179. list_del(&edev->entry);
  180. mutex_unlock(&extcon_dev_list_lock);
  181. if (!skip && get_device(edev->dev)) {
  182. device_unregister(edev->dev);
  183. put_device(edev->dev);
  184. }
  185. kfree(edev->dev);
  186. }
  187. static void extcon_dev_release(struct device *dev)
  188. {
  189. struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
  190. extcon_cleanup(edev, true);
  191. }
  192. /**
  193. * extcon_dev_register() - Register a new extcon device
  194. * @edev : the new extcon device (should be allocated before calling)
  195. * @dev : the parent device for this extcon device.
  196. *
  197. * Among the members of edev struct, please set the "user initializing data"
  198. * in any case and set the "optional callbacks" if required. However, please
  199. * do not set the values of "internal data", which are initialized by
  200. * this function.
  201. */
  202. int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
  203. {
  204. int ret;
  205. if (!extcon_class) {
  206. ret = create_extcon_class();
  207. if (ret < 0)
  208. return ret;
  209. }
  210. edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
  211. edev->dev->parent = dev;
  212. edev->dev->class = extcon_class;
  213. edev->dev->release = extcon_dev_release;
  214. dev_set_name(edev->dev, edev->name ? edev->name : dev_name(dev));
  215. ret = device_register(edev->dev);
  216. if (ret) {
  217. put_device(edev->dev);
  218. goto err_dev;
  219. }
  220. #if defined(CONFIG_ANDROID) && !defined(CONFIG_ANDROID_SWITCH)
  221. if (switch_class)
  222. ret = class_compat_create_link(switch_class, edev->dev,
  223. dev);
  224. #endif /* CONFIG_ANDROID && !defined(CONFIG_ANDROID_SWITCH) */
  225. RAW_INIT_NOTIFIER_HEAD(&edev->nh);
  226. dev_set_drvdata(edev->dev, edev);
  227. edev->state = 0;
  228. mutex_lock(&extcon_dev_list_lock);
  229. list_add(&edev->entry, &extcon_dev_list);
  230. mutex_unlock(&extcon_dev_list_lock);
  231. return 0;
  232. err_dev:
  233. kfree(edev->dev);
  234. return ret;
  235. }
  236. EXPORT_SYMBOL_GPL(extcon_dev_register);
  237. /**
  238. * extcon_dev_unregister() - Unregister the extcon device.
  239. * @edev: the extcon device instance to be unregitered.
  240. *
  241. * Note that this does not call kfree(edev) because edev was not allocated
  242. * by this class.
  243. */
  244. void extcon_dev_unregister(struct extcon_dev *edev)
  245. {
  246. extcon_cleanup(edev, false);
  247. }
  248. EXPORT_SYMBOL_GPL(extcon_dev_unregister);
  249. static int __init extcon_class_init(void)
  250. {
  251. return create_extcon_class();
  252. }
  253. module_init(extcon_class_init);
  254. static void __exit extcon_class_exit(void)
  255. {
  256. class_destroy(extcon_class);
  257. }
  258. module_exit(extcon_class_exit);
  259. MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
  260. MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
  261. MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
  262. MODULE_DESCRIPTION("External connector (extcon) class driver");
  263. MODULE_LICENSE("GPL");