host_notify_class.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. *
  4. * Copyright (C) 2011-2023 Samsung, Inc.
  5. * Author: Dongrak Shin <[email protected]>
  6. *
  7. */
  8. /* usb notify layer v4.0 */
  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/host_notify.h>
  17. #if defined(CONFIG_USB_HW_PARAM)
  18. #include <linux/usb_notify.h>
  19. #endif
  20. struct notify_data {
  21. struct class *host_notify_class;
  22. atomic_t device_count;
  23. struct mutex host_notify_lock;
  24. };
  25. static struct notify_data host_notify;
  26. static ssize_t mode_show(
  27. struct device *dev, struct device_attribute *attr, char *buf)
  28. {
  29. struct host_notify_dev *ndev = (struct host_notify_dev *)
  30. dev_get_drvdata(dev);
  31. char *mode;
  32. switch (ndev->mode) {
  33. case NOTIFY_HOST_MODE:
  34. mode = "HOST";
  35. break;
  36. case NOTIFY_PERIPHERAL_MODE:
  37. mode = "PERIPHERAL";
  38. break;
  39. case NOTIFY_TEST_MODE:
  40. mode = "TEST";
  41. break;
  42. case NOTIFY_NONE_MODE:
  43. default:
  44. mode = "NONE";
  45. break;
  46. }
  47. return sprintf(buf, "%s\n", mode);
  48. }
  49. static ssize_t mode_store(
  50. struct device *dev, struct device_attribute *attr,
  51. const char *buf, size_t size)
  52. {
  53. #ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
  54. struct host_notify_dev *ndev = (struct host_notify_dev *)
  55. dev_get_drvdata(dev);
  56. #endif
  57. char *mode;
  58. size_t ret = -ENOMEM;
  59. int sret = 0;
  60. if (size < strlen(buf))
  61. goto error;
  62. mode = kzalloc(size+1, GFP_KERNEL);
  63. if (!mode)
  64. goto error;
  65. sret = sscanf(buf, "%s", mode);
  66. if (sret != 1)
  67. goto error1;
  68. #ifndef CONFIG_SAMSUNG_PRODUCT_SHIP
  69. if (ndev->set_mode) {
  70. unl_info("host_notify: set mode %s\n", mode);
  71. if (!strncmp(mode, "HOST", 4))
  72. ndev->set_mode(NOTIFY_SET_ON);
  73. else if (!strncmp(mode, "NONE", 4))
  74. ndev->set_mode(NOTIFY_SET_OFF);
  75. }
  76. #endif
  77. ret = size;
  78. error1:
  79. kfree(mode);
  80. error:
  81. return ret;
  82. }
  83. static ssize_t booster_show(struct device *dev, struct device_attribute *attr,
  84. char *buf)
  85. {
  86. struct host_notify_dev *ndev = (struct host_notify_dev *)
  87. dev_get_drvdata(dev);
  88. char *booster;
  89. switch (ndev->booster) {
  90. case NOTIFY_POWER_ON:
  91. booster = "ON";
  92. break;
  93. case NOTIFY_POWER_OFF:
  94. default:
  95. booster = "OFF";
  96. break;
  97. }
  98. unl_info("host_notify: read booster %s\n", booster);
  99. return sprintf(buf, "%s\n", booster);
  100. }
  101. static ssize_t booster_store(
  102. struct device *dev, struct device_attribute *attr,
  103. const char *buf, size_t size)
  104. {
  105. struct host_notify_dev *ndev = (struct host_notify_dev *)
  106. dev_get_drvdata(dev);
  107. char *booster;
  108. size_t ret = -ENOMEM;
  109. int sret = 0;
  110. if (size < strlen(buf))
  111. goto error;
  112. booster = kzalloc(size+1, GFP_KERNEL);
  113. if (!booster)
  114. goto error;
  115. sret = sscanf(buf, "%s", booster);
  116. if (sret != 1)
  117. goto error1;
  118. if (ndev->set_booster) {
  119. unl_info("host_notify: set booster %s\n", booster);
  120. if (!strncmp(booster, "ON", 2)) {
  121. ndev->set_booster(NOTIFY_SET_ON);
  122. ndev->mode = NOTIFY_TEST_MODE;
  123. } else if (!strncmp(booster, "OFF", 3)) {
  124. ndev->set_booster(NOTIFY_SET_OFF);
  125. ndev->mode = NOTIFY_NONE_MODE;
  126. }
  127. }
  128. ret = size;
  129. error1:
  130. kfree(booster);
  131. error:
  132. return ret;
  133. }
  134. static DEVICE_ATTR_RW(mode);
  135. static DEVICE_ATTR_RW(booster);
  136. static struct attribute *host_notify_attrs[] = {
  137. &dev_attr_mode.attr,
  138. &dev_attr_booster.attr,
  139. NULL,
  140. };
  141. static struct attribute_group host_notify_attr_grp = {
  142. .attrs = host_notify_attrs,
  143. };
  144. char *host_state_string(int type)
  145. {
  146. switch (type) {
  147. case NOTIFY_HOST_NONE: return "none";
  148. case NOTIFY_HOST_ADD: return "add";
  149. case NOTIFY_HOST_REMOVE: return "remove";
  150. case NOTIFY_HOST_OVERCURRENT: return "overcurrent";
  151. case NOTIFY_HOST_LOWBATT: return "lowbatt";
  152. case NOTIFY_HOST_BLOCK: return "block";
  153. case NOTIFY_HOST_SOURCE: return "source";
  154. case NOTIFY_HOST_SINK: return "sink";
  155. case NOTIFY_HOST_UNKNOWN:
  156. default: return "unknown";
  157. }
  158. }
  159. static int check_state_type(int state)
  160. {
  161. int ret = 0;
  162. switch (state) {
  163. case NOTIFY_HOST_ADD:
  164. case NOTIFY_HOST_REMOVE:
  165. case NOTIFY_HOST_BLOCK:
  166. ret = NOTIFY_HOST_STATE;
  167. break;
  168. case NOTIFY_HOST_OVERCURRENT:
  169. case NOTIFY_HOST_LOWBATT:
  170. case NOTIFY_HOST_SOURCE:
  171. case NOTIFY_HOST_SINK:
  172. ret = NOTIFY_POWER_STATE;
  173. break;
  174. case NOTIFY_HOST_NONE:
  175. case NOTIFY_HOST_UNKNOWN:
  176. default:
  177. ret = NOTIFY_UNKNOWN_STATE;
  178. break;
  179. }
  180. return ret;
  181. }
  182. int host_state_notify(struct host_notify_dev *ndev, int state)
  183. {
  184. int type = 0;
  185. if (!ndev->dev) {
  186. unl_err("host_notify: %s ndev->dev is NULL\n", __func__);
  187. return -ENXIO;
  188. }
  189. mutex_lock(&host_notify.host_notify_lock);
  190. unl_info("host_notify: ndev name=%s: state=%s\n",
  191. ndev->name, host_state_string(state));
  192. type = check_state_type(state);
  193. if (type == NOTIFY_HOST_STATE) {
  194. if (ndev->host_state != state) {
  195. unl_info("host_notify: host_state (%s->%s)\n",
  196. host_state_string(ndev->host_state),
  197. host_state_string(state));
  198. ndev->host_state = state;
  199. ndev->host_change = 1;
  200. kobject_uevent(&ndev->dev->kobj, KOBJ_CHANGE);
  201. ndev->host_change = 0;
  202. #if defined(CONFIG_USB_HW_PARAM)
  203. if (state == NOTIFY_HOST_ADD)
  204. inc_hw_param_host(ndev, USB_CCIC_OTG_USE_COUNT);
  205. #endif
  206. }
  207. } else if (type == NOTIFY_POWER_STATE) {
  208. if (ndev->power_state != state) {
  209. unl_info("host_notify: power_state (%s->%s)\n",
  210. host_state_string(ndev->power_state),
  211. host_state_string(state));
  212. ndev->power_state = state;
  213. ndev->power_change = 1;
  214. kobject_uevent(&ndev->dev->kobj, KOBJ_CHANGE);
  215. ndev->power_change = 0;
  216. #if defined(CONFIG_USB_HW_PARAM)
  217. if (state == NOTIFY_HOST_OVERCURRENT)
  218. inc_hw_param_host(ndev, USB_CCIC_OVC_COUNT);
  219. #endif
  220. }
  221. } else {
  222. ndev->host_state = state;
  223. ndev->power_state = state;
  224. }
  225. mutex_unlock(&host_notify.host_notify_lock);
  226. return 0;
  227. }
  228. EXPORT_SYMBOL_GPL(host_state_notify);
  229. static int
  230. host_notify_uevent(struct device *dev, struct kobj_uevent_env *env)
  231. {
  232. struct host_notify_dev *ndev = (struct host_notify_dev *)
  233. dev_get_drvdata(dev);
  234. char *state;
  235. int state_type;
  236. if (!ndev) {
  237. /* this happens when the device is first created */
  238. return 0;
  239. }
  240. if (ndev->host_change)
  241. state_type = ndev->host_state;
  242. else if (ndev->power_change)
  243. state_type = ndev->power_state;
  244. else
  245. state_type = NOTIFY_HOST_NONE;
  246. switch (state_type) {
  247. case NOTIFY_HOST_ADD:
  248. state = "ADD";
  249. break;
  250. case NOTIFY_HOST_REMOVE:
  251. state = "REMOVE";
  252. break;
  253. case NOTIFY_HOST_OVERCURRENT:
  254. state = "OVERCURRENT";
  255. break;
  256. case NOTIFY_HOST_LOWBATT:
  257. state = "LOWBATT";
  258. break;
  259. case NOTIFY_HOST_BLOCK:
  260. state = "BLOCK";
  261. break;
  262. case NOTIFY_HOST_SOURCE:
  263. state = "SOURCE";
  264. break;
  265. case NOTIFY_HOST_SINK:
  266. state = "SINK";
  267. break;
  268. case NOTIFY_HOST_UNKNOWN:
  269. state = "UNKNOWN";
  270. break;
  271. case NOTIFY_HOST_NONE:
  272. default:
  273. return 0;
  274. }
  275. if (add_uevent_var(env, "DEVNAME=%s", ndev->dev->kobj.name))
  276. return -ENOMEM;
  277. if (add_uevent_var(env, "STATE=%s", state))
  278. return -ENOMEM;
  279. return 0;
  280. }
  281. static int create_notify_class(void)
  282. {
  283. if (!host_notify.host_notify_class) {
  284. host_notify.host_notify_class
  285. = class_create(THIS_MODULE, "host_notify");
  286. if (IS_ERR(host_notify.host_notify_class))
  287. return PTR_ERR(host_notify.host_notify_class);
  288. atomic_set(&host_notify.device_count, 0);
  289. mutex_init(&host_notify.host_notify_lock);
  290. host_notify.host_notify_class->dev_uevent = host_notify_uevent;
  291. }
  292. return 0;
  293. }
  294. int host_notify_dev_register(struct host_notify_dev *ndev)
  295. {
  296. int ret;
  297. if (!host_notify.host_notify_class) {
  298. ret = create_notify_class();
  299. if (ret < 0)
  300. return ret;
  301. }
  302. ndev->index = atomic_inc_return(&host_notify.device_count);
  303. ndev->dev = device_create(host_notify.host_notify_class, NULL,
  304. MKDEV(0, ndev->index), NULL, "%s", ndev->name);
  305. if (IS_ERR(ndev->dev))
  306. return PTR_ERR(ndev->dev);
  307. ret = sysfs_create_group(&ndev->dev->kobj, &host_notify_attr_grp);
  308. if (ret < 0) {
  309. device_destroy(host_notify.host_notify_class,
  310. MKDEV(0, ndev->index));
  311. return ret;
  312. }
  313. dev_set_drvdata(ndev->dev, ndev);
  314. ndev->host_state = NOTIFY_HOST_NONE;
  315. ndev->power_state = NOTIFY_HOST_SINK;
  316. return 0;
  317. }
  318. EXPORT_SYMBOL_GPL(host_notify_dev_register);
  319. void host_notify_dev_unregister(struct host_notify_dev *ndev)
  320. {
  321. ndev->host_state = NOTIFY_HOST_NONE;
  322. ndev->power_state = NOTIFY_HOST_SINK;
  323. sysfs_remove_group(&ndev->dev->kobj, &host_notify_attr_grp);
  324. dev_set_drvdata(ndev->dev, NULL);
  325. device_destroy(host_notify.host_notify_class, MKDEV(0, ndev->index));
  326. ndev->dev = NULL;
  327. }
  328. EXPORT_SYMBOL_GPL(host_notify_dev_unregister);
  329. int notify_class_init(void)
  330. {
  331. return create_notify_class();
  332. }
  333. void notify_class_exit(void)
  334. {
  335. class_destroy(host_notify.host_notify_class);
  336. }