ledtrig-usbport.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * USB port LED trigger
  4. *
  5. * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl>
  6. */
  7. #include <linux/device.h>
  8. #include <linux/leds.h>
  9. #include <linux/module.h>
  10. #include <linux/of.h>
  11. #include <linux/slab.h>
  12. #include <linux/usb.h>
  13. #include <linux/usb/of.h>
  14. struct usbport_trig_data {
  15. struct led_classdev *led_cdev;
  16. struct list_head ports;
  17. struct notifier_block nb;
  18. int count; /* Amount of connected matching devices */
  19. };
  20. struct usbport_trig_port {
  21. struct usbport_trig_data *data;
  22. struct usb_device *hub;
  23. int portnum;
  24. char *port_name;
  25. bool observed;
  26. struct device_attribute attr;
  27. struct list_head list;
  28. };
  29. /***************************************
  30. * Helpers
  31. ***************************************/
  32. /**
  33. * usbport_trig_usb_dev_observed - Check if dev is connected to observed port
  34. */
  35. static bool usbport_trig_usb_dev_observed(struct usbport_trig_data *usbport_data,
  36. struct usb_device *usb_dev)
  37. {
  38. struct usbport_trig_port *port;
  39. if (!usb_dev->parent)
  40. return false;
  41. list_for_each_entry(port, &usbport_data->ports, list) {
  42. if (usb_dev->parent == port->hub &&
  43. usb_dev->portnum == port->portnum)
  44. return port->observed;
  45. }
  46. return false;
  47. }
  48. static int usbport_trig_usb_dev_check(struct usb_device *usb_dev, void *data)
  49. {
  50. struct usbport_trig_data *usbport_data = data;
  51. if (usbport_trig_usb_dev_observed(usbport_data, usb_dev))
  52. usbport_data->count++;
  53. return 0;
  54. }
  55. /**
  56. * usbport_trig_update_count - Recalculate amount of connected matching devices
  57. */
  58. static void usbport_trig_update_count(struct usbport_trig_data *usbport_data)
  59. {
  60. struct led_classdev *led_cdev = usbport_data->led_cdev;
  61. usbport_data->count = 0;
  62. usb_for_each_dev(usbport_data, usbport_trig_usb_dev_check);
  63. led_set_brightness(led_cdev, usbport_data->count ? LED_FULL : LED_OFF);
  64. }
  65. /***************************************
  66. * Device attr
  67. ***************************************/
  68. static ssize_t usbport_trig_port_show(struct device *dev,
  69. struct device_attribute *attr, char *buf)
  70. {
  71. struct usbport_trig_port *port = container_of(attr,
  72. struct usbport_trig_port,
  73. attr);
  74. return sprintf(buf, "%d\n", port->observed) + 1;
  75. }
  76. static ssize_t usbport_trig_port_store(struct device *dev,
  77. struct device_attribute *attr,
  78. const char *buf, size_t size)
  79. {
  80. struct usbport_trig_port *port = container_of(attr,
  81. struct usbport_trig_port,
  82. attr);
  83. if (!strcmp(buf, "0") || !strcmp(buf, "0\n"))
  84. port->observed = 0;
  85. else if (!strcmp(buf, "1") || !strcmp(buf, "1\n"))
  86. port->observed = 1;
  87. else
  88. return -EINVAL;
  89. usbport_trig_update_count(port->data);
  90. return size;
  91. }
  92. static struct attribute *ports_attrs[] = {
  93. NULL,
  94. };
  95. static const struct attribute_group ports_group = {
  96. .name = "ports",
  97. .attrs = ports_attrs,
  98. };
  99. static const struct attribute_group *ports_groups[] = {
  100. &ports_group,
  101. NULL
  102. };
  103. /***************************************
  104. * Adding & removing ports
  105. ***************************************/
  106. /**
  107. * usbport_trig_port_observed - Check if port should be observed
  108. */
  109. static bool usbport_trig_port_observed(struct usbport_trig_data *usbport_data,
  110. struct usb_device *usb_dev, int port1)
  111. {
  112. struct device *dev = usbport_data->led_cdev->dev;
  113. struct device_node *led_np = dev->of_node;
  114. struct of_phandle_args args;
  115. struct device_node *port_np;
  116. int count, i;
  117. if (!led_np)
  118. return false;
  119. /*
  120. * Get node of port being added
  121. *
  122. * FIXME: This is really the device node of the connected device
  123. */
  124. port_np = usb_of_get_device_node(usb_dev, port1);
  125. if (!port_np)
  126. return false;
  127. of_node_put(port_np);
  128. /* Amount of trigger sources for this LED */
  129. count = of_count_phandle_with_args(led_np, "trigger-sources",
  130. "#trigger-source-cells");
  131. if (count < 0) {
  132. dev_warn(dev, "Failed to get trigger sources for %pOF\n",
  133. led_np);
  134. return false;
  135. }
  136. /* Check list of sources for this specific port */
  137. for (i = 0; i < count; i++) {
  138. int err;
  139. err = of_parse_phandle_with_args(led_np, "trigger-sources",
  140. "#trigger-source-cells", i,
  141. &args);
  142. if (err) {
  143. dev_err(dev, "Failed to get trigger source phandle at index %d: %d\n",
  144. i, err);
  145. continue;
  146. }
  147. of_node_put(args.np);
  148. if (args.np == port_np)
  149. return true;
  150. }
  151. return false;
  152. }
  153. static int usbport_trig_add_port(struct usbport_trig_data *usbport_data,
  154. struct usb_device *usb_dev,
  155. const char *hub_name, int portnum)
  156. {
  157. struct led_classdev *led_cdev = usbport_data->led_cdev;
  158. struct usbport_trig_port *port;
  159. size_t len;
  160. int err;
  161. port = kzalloc(sizeof(*port), GFP_KERNEL);
  162. if (!port) {
  163. err = -ENOMEM;
  164. goto err_out;
  165. }
  166. port->data = usbport_data;
  167. port->hub = usb_dev;
  168. port->portnum = portnum;
  169. port->observed = usbport_trig_port_observed(usbport_data, usb_dev,
  170. portnum);
  171. len = strlen(hub_name) + 8;
  172. port->port_name = kzalloc(len, GFP_KERNEL);
  173. if (!port->port_name) {
  174. err = -ENOMEM;
  175. goto err_free_port;
  176. }
  177. snprintf(port->port_name, len, "%s-port%d", hub_name, portnum);
  178. sysfs_attr_init(&port->attr.attr);
  179. port->attr.attr.name = port->port_name;
  180. port->attr.attr.mode = S_IRUSR | S_IWUSR;
  181. port->attr.show = usbport_trig_port_show;
  182. port->attr.store = usbport_trig_port_store;
  183. err = sysfs_add_file_to_group(&led_cdev->dev->kobj, &port->attr.attr,
  184. ports_group.name);
  185. if (err)
  186. goto err_free_port_name;
  187. list_add_tail(&port->list, &usbport_data->ports);
  188. return 0;
  189. err_free_port_name:
  190. kfree(port->port_name);
  191. err_free_port:
  192. kfree(port);
  193. err_out:
  194. return err;
  195. }
  196. static int usbport_trig_add_usb_dev_ports(struct usb_device *usb_dev,
  197. void *data)
  198. {
  199. struct usbport_trig_data *usbport_data = data;
  200. int i;
  201. for (i = 1; i <= usb_dev->maxchild; i++)
  202. usbport_trig_add_port(usbport_data, usb_dev,
  203. dev_name(&usb_dev->dev), i);
  204. return 0;
  205. }
  206. static void usbport_trig_remove_port(struct usbport_trig_data *usbport_data,
  207. struct usbport_trig_port *port)
  208. {
  209. struct led_classdev *led_cdev = usbport_data->led_cdev;
  210. list_del(&port->list);
  211. sysfs_remove_file_from_group(&led_cdev->dev->kobj, &port->attr.attr,
  212. ports_group.name);
  213. kfree(port->port_name);
  214. kfree(port);
  215. }
  216. static void usbport_trig_remove_usb_dev_ports(struct usbport_trig_data *usbport_data,
  217. struct usb_device *usb_dev)
  218. {
  219. struct usbport_trig_port *port, *tmp;
  220. list_for_each_entry_safe(port, tmp, &usbport_data->ports, list) {
  221. if (port->hub == usb_dev)
  222. usbport_trig_remove_port(usbport_data, port);
  223. }
  224. }
  225. /***************************************
  226. * Init, exit, etc.
  227. ***************************************/
  228. static int usbport_trig_notify(struct notifier_block *nb, unsigned long action,
  229. void *data)
  230. {
  231. struct usbport_trig_data *usbport_data =
  232. container_of(nb, struct usbport_trig_data, nb);
  233. struct led_classdev *led_cdev = usbport_data->led_cdev;
  234. struct usb_device *usb_dev = data;
  235. bool observed;
  236. observed = usbport_trig_usb_dev_observed(usbport_data, usb_dev);
  237. switch (action) {
  238. case USB_DEVICE_ADD:
  239. usbport_trig_add_usb_dev_ports(usb_dev, usbport_data);
  240. if (observed && usbport_data->count++ == 0)
  241. led_set_brightness(led_cdev, LED_FULL);
  242. return NOTIFY_OK;
  243. case USB_DEVICE_REMOVE:
  244. usbport_trig_remove_usb_dev_ports(usbport_data, usb_dev);
  245. if (observed && --usbport_data->count == 0)
  246. led_set_brightness(led_cdev, LED_OFF);
  247. return NOTIFY_OK;
  248. }
  249. return NOTIFY_DONE;
  250. }
  251. static int usbport_trig_activate(struct led_classdev *led_cdev)
  252. {
  253. struct usbport_trig_data *usbport_data;
  254. usbport_data = kzalloc(sizeof(*usbport_data), GFP_KERNEL);
  255. if (!usbport_data)
  256. return -ENOMEM;
  257. usbport_data->led_cdev = led_cdev;
  258. /* List of ports */
  259. INIT_LIST_HEAD(&usbport_data->ports);
  260. usb_for_each_dev(usbport_data, usbport_trig_add_usb_dev_ports);
  261. usbport_trig_update_count(usbport_data);
  262. /* Notifications */
  263. usbport_data->nb.notifier_call = usbport_trig_notify;
  264. led_set_trigger_data(led_cdev, usbport_data);
  265. usb_register_notify(&usbport_data->nb);
  266. return 0;
  267. }
  268. static void usbport_trig_deactivate(struct led_classdev *led_cdev)
  269. {
  270. struct usbport_trig_data *usbport_data = led_get_trigger_data(led_cdev);
  271. struct usbport_trig_port *port, *tmp;
  272. list_for_each_entry_safe(port, tmp, &usbport_data->ports, list) {
  273. usbport_trig_remove_port(usbport_data, port);
  274. }
  275. usb_unregister_notify(&usbport_data->nb);
  276. kfree(usbport_data);
  277. }
  278. static struct led_trigger usbport_led_trigger = {
  279. .name = "usbport",
  280. .activate = usbport_trig_activate,
  281. .deactivate = usbport_trig_deactivate,
  282. .groups = ports_groups,
  283. };
  284. static int __init usbport_trig_init(void)
  285. {
  286. return led_trigger_register(&usbport_led_trigger);
  287. }
  288. static void __exit usbport_trig_exit(void)
  289. {
  290. led_trigger_unregister(&usbport_led_trigger);
  291. }
  292. module_init(usbport_trig_init);
  293. module_exit(usbport_trig_exit);
  294. MODULE_AUTHOR("Rafał Miłecki <rafal@milecki.pl>");
  295. MODULE_DESCRIPTION("USB port trigger");
  296. MODULE_LICENSE("GPL v2");