ledtrig-usbport.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  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. /***************************************
  100. * Adding & removing ports
  101. ***************************************/
  102. /**
  103. * usbport_trig_port_observed - Check if port should be observed
  104. */
  105. static bool usbport_trig_port_observed(struct usbport_trig_data *usbport_data,
  106. struct usb_device *usb_dev, int port1)
  107. {
  108. struct device *dev = usbport_data->led_cdev->dev;
  109. struct device_node *led_np = dev->of_node;
  110. struct of_phandle_args args;
  111. struct device_node *port_np;
  112. int count, i;
  113. if (!led_np)
  114. return false;
  115. /* Get node of port being added */
  116. port_np = usb_of_get_child_node(usb_dev->dev.of_node, port1);
  117. if (!port_np)
  118. return false;
  119. /* Amount of trigger sources for this LED */
  120. count = of_count_phandle_with_args(led_np, "trigger-sources",
  121. "#trigger-source-cells");
  122. if (count < 0) {
  123. dev_warn(dev, "Failed to get trigger sources for %pOF\n",
  124. led_np);
  125. return false;
  126. }
  127. /* Check list of sources for this specific port */
  128. for (i = 0; i < count; i++) {
  129. int err;
  130. err = of_parse_phandle_with_args(led_np, "trigger-sources",
  131. "#trigger-source-cells", i,
  132. &args);
  133. if (err) {
  134. dev_err(dev, "Failed to get trigger source phandle at index %d: %d\n",
  135. i, err);
  136. continue;
  137. }
  138. of_node_put(args.np);
  139. if (args.np == port_np)
  140. return true;
  141. }
  142. return false;
  143. }
  144. static int usbport_trig_add_port(struct usbport_trig_data *usbport_data,
  145. struct usb_device *usb_dev,
  146. const char *hub_name, int portnum)
  147. {
  148. struct led_classdev *led_cdev = usbport_data->led_cdev;
  149. struct usbport_trig_port *port;
  150. size_t len;
  151. int err;
  152. port = kzalloc(sizeof(*port), GFP_KERNEL);
  153. if (!port) {
  154. err = -ENOMEM;
  155. goto err_out;
  156. }
  157. port->data = usbport_data;
  158. port->hub = usb_dev;
  159. port->portnum = portnum;
  160. port->observed = usbport_trig_port_observed(usbport_data, usb_dev,
  161. portnum);
  162. len = strlen(hub_name) + 8;
  163. port->port_name = kzalloc(len, GFP_KERNEL);
  164. if (!port->port_name) {
  165. err = -ENOMEM;
  166. goto err_free_port;
  167. }
  168. snprintf(port->port_name, len, "%s-port%d", hub_name, portnum);
  169. sysfs_attr_init(&port->attr.attr);
  170. port->attr.attr.name = port->port_name;
  171. port->attr.attr.mode = S_IRUSR | S_IWUSR;
  172. port->attr.show = usbport_trig_port_show;
  173. port->attr.store = usbport_trig_port_store;
  174. err = sysfs_add_file_to_group(&led_cdev->dev->kobj, &port->attr.attr,
  175. ports_group.name);
  176. if (err)
  177. goto err_free_port_name;
  178. list_add_tail(&port->list, &usbport_data->ports);
  179. return 0;
  180. err_free_port_name:
  181. kfree(port->port_name);
  182. err_free_port:
  183. kfree(port);
  184. err_out:
  185. return err;
  186. }
  187. static int usbport_trig_add_usb_dev_ports(struct usb_device *usb_dev,
  188. void *data)
  189. {
  190. struct usbport_trig_data *usbport_data = data;
  191. int i;
  192. for (i = 1; i <= usb_dev->maxchild; i++)
  193. usbport_trig_add_port(usbport_data, usb_dev,
  194. dev_name(&usb_dev->dev), i);
  195. return 0;
  196. }
  197. static void usbport_trig_remove_port(struct usbport_trig_data *usbport_data,
  198. struct usbport_trig_port *port)
  199. {
  200. struct led_classdev *led_cdev = usbport_data->led_cdev;
  201. list_del(&port->list);
  202. sysfs_remove_file_from_group(&led_cdev->dev->kobj, &port->attr.attr,
  203. ports_group.name);
  204. kfree(port->port_name);
  205. kfree(port);
  206. }
  207. static void usbport_trig_remove_usb_dev_ports(struct usbport_trig_data *usbport_data,
  208. struct usb_device *usb_dev)
  209. {
  210. struct usbport_trig_port *port, *tmp;
  211. list_for_each_entry_safe(port, tmp, &usbport_data->ports, list) {
  212. if (port->hub == usb_dev)
  213. usbport_trig_remove_port(usbport_data, port);
  214. }
  215. }
  216. /***************************************
  217. * Init, exit, etc.
  218. ***************************************/
  219. static int usbport_trig_notify(struct notifier_block *nb, unsigned long action,
  220. void *data)
  221. {
  222. struct usbport_trig_data *usbport_data =
  223. container_of(nb, struct usbport_trig_data, nb);
  224. struct led_classdev *led_cdev = usbport_data->led_cdev;
  225. struct usb_device *usb_dev = data;
  226. bool observed;
  227. observed = usbport_trig_usb_dev_observed(usbport_data, usb_dev);
  228. switch (action) {
  229. case USB_DEVICE_ADD:
  230. usbport_trig_add_usb_dev_ports(usb_dev, usbport_data);
  231. if (observed && usbport_data->count++ == 0)
  232. led_set_brightness(led_cdev, LED_FULL);
  233. return NOTIFY_OK;
  234. case USB_DEVICE_REMOVE:
  235. usbport_trig_remove_usb_dev_ports(usbport_data, usb_dev);
  236. if (observed && --usbport_data->count == 0)
  237. led_set_brightness(led_cdev, LED_OFF);
  238. return NOTIFY_OK;
  239. }
  240. return NOTIFY_DONE;
  241. }
  242. static void usbport_trig_activate(struct led_classdev *led_cdev)
  243. {
  244. struct usbport_trig_data *usbport_data;
  245. int err;
  246. usbport_data = kzalloc(sizeof(*usbport_data), GFP_KERNEL);
  247. if (!usbport_data)
  248. return;
  249. usbport_data->led_cdev = led_cdev;
  250. /* List of ports */
  251. INIT_LIST_HEAD(&usbport_data->ports);
  252. err = sysfs_create_group(&led_cdev->dev->kobj, &ports_group);
  253. if (err)
  254. goto err_free;
  255. usb_for_each_dev(usbport_data, usbport_trig_add_usb_dev_ports);
  256. usbport_trig_update_count(usbport_data);
  257. /* Notifications */
  258. usbport_data->nb.notifier_call = usbport_trig_notify,
  259. led_cdev->trigger_data = usbport_data;
  260. usb_register_notify(&usbport_data->nb);
  261. led_cdev->activated = true;
  262. return;
  263. err_free:
  264. kfree(usbport_data);
  265. }
  266. static void usbport_trig_deactivate(struct led_classdev *led_cdev)
  267. {
  268. struct usbport_trig_data *usbport_data = led_cdev->trigger_data;
  269. struct usbport_trig_port *port, *tmp;
  270. if (!led_cdev->activated)
  271. return;
  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. sysfs_remove_group(&led_cdev->dev->kobj, &ports_group);
  277. kfree(usbport_data);
  278. led_cdev->activated = false;
  279. }
  280. static struct led_trigger usbport_led_trigger = {
  281. .name = "usbport",
  282. .activate = usbport_trig_activate,
  283. .deactivate = usbport_trig_deactivate,
  284. };
  285. static int __init usbport_trig_init(void)
  286. {
  287. return led_trigger_register(&usbport_led_trigger);
  288. }
  289. static void __exit usbport_trig_exit(void)
  290. {
  291. led_trigger_unregister(&usbport_led_trigger);
  292. }
  293. module_init(usbport_trig_init);
  294. module_exit(usbport_trig_exit);
  295. MODULE_AUTHOR("Rafał Miłecki <rafal@milecki.pl>");
  296. MODULE_DESCRIPTION("USB port trigger");
  297. MODULE_LICENSE("GPL v2");