1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl>
8 #include <linux/device.h>
9 #include <linux/leds.h>
10 #include <linux/module.h>
12 #include <linux/slab.h>
13 #include <linux/usb.h>
14 #include <linux/usb/of.h>
16 struct usbport_trig_data
{
17 struct led_classdev
*led_cdev
;
18 struct list_head ports
;
19 struct notifier_block nb
;
20 int count
; /* Amount of connected matching devices */
23 struct usbport_trig_port
{
24 struct usbport_trig_data
*data
;
25 struct usb_device
*hub
;
29 struct device_attribute attr
;
30 struct list_head list
;
33 /***************************************
35 ***************************************/
38 * usbport_trig_usb_dev_observed - Check if dev is connected to observed port
40 static bool usbport_trig_usb_dev_observed(struct usbport_trig_data
*usbport_data
,
41 struct usb_device
*usb_dev
)
43 struct usbport_trig_port
*port
;
48 list_for_each_entry(port
, &usbport_data
->ports
, list
) {
49 if (usb_dev
->parent
== port
->hub
&&
50 usb_dev
->portnum
== port
->portnum
)
51 return port
->observed
;
57 static int usbport_trig_usb_dev_check(struct usb_device
*usb_dev
, void *data
)
59 struct usbport_trig_data
*usbport_data
= data
;
61 if (usbport_trig_usb_dev_observed(usbport_data
, usb_dev
))
62 usbport_data
->count
++;
68 * usbport_trig_update_count - Recalculate amount of connected matching devices
70 static void usbport_trig_update_count(struct usbport_trig_data
*usbport_data
)
72 struct led_classdev
*led_cdev
= usbport_data
->led_cdev
;
74 usbport_data
->count
= 0;
75 usb_for_each_dev(usbport_data
, usbport_trig_usb_dev_check
);
76 led_set_brightness(led_cdev
, usbport_data
->count
? LED_FULL
: LED_OFF
);
79 /***************************************
81 ***************************************/
83 static ssize_t
usbport_trig_port_show(struct device
*dev
,
84 struct device_attribute
*attr
, char *buf
)
86 struct usbport_trig_port
*port
= container_of(attr
,
87 struct usbport_trig_port
,
90 return sprintf(buf
, "%d\n", port
->observed
) + 1;
93 static ssize_t
usbport_trig_port_store(struct device
*dev
,
94 struct device_attribute
*attr
,
95 const char *buf
, size_t size
)
97 struct usbport_trig_port
*port
= container_of(attr
,
98 struct usbport_trig_port
,
101 if (!strcmp(buf
, "0") || !strcmp(buf
, "0\n"))
103 else if (!strcmp(buf
, "1") || !strcmp(buf
, "1\n"))
108 usbport_trig_update_count(port
->data
);
113 static struct attribute
*ports_attrs
[] = {
117 static const struct attribute_group ports_group
= {
119 .attrs
= ports_attrs
,
122 /***************************************
123 * Adding & removing ports
124 ***************************************/
127 * usbport_trig_port_observed - Check if port should be observed
129 static bool usbport_trig_port_observed(struct usbport_trig_data
*usbport_data
,
130 struct usb_device
*usb_dev
, int port1
)
132 struct device
*dev
= usbport_data
->led_cdev
->dev
;
133 struct device_node
*led_np
= dev
->of_node
;
134 struct of_phandle_args args
;
135 struct device_node
*port_np
;
142 * Get node of port being added
144 * FIXME: This is really the device node of the connected device
146 port_np
= usb_of_get_device_node(usb_dev
, port1
);
150 of_node_put(port_np
);
152 /* Amount of trigger sources for this LED */
153 count
= of_count_phandle_with_args(led_np
, "trigger-sources",
154 "#trigger-source-cells");
156 dev_warn(dev
, "Failed to get trigger sources for %pOF\n",
161 /* Check list of sources for this specific port */
162 for (i
= 0; i
< count
; i
++) {
165 err
= of_parse_phandle_with_args(led_np
, "trigger-sources",
166 "#trigger-source-cells", i
,
169 dev_err(dev
, "Failed to get trigger source phandle at index %d: %d\n",
174 of_node_put(args
.np
);
176 if (args
.np
== port_np
)
183 static int usbport_trig_add_port(struct usbport_trig_data
*usbport_data
,
184 struct usb_device
*usb_dev
,
185 const char *hub_name
, int portnum
)
187 struct led_classdev
*led_cdev
= usbport_data
->led_cdev
;
188 struct usbport_trig_port
*port
;
192 port
= kzalloc(sizeof(*port
), GFP_KERNEL
);
198 port
->data
= usbport_data
;
200 port
->portnum
= portnum
;
201 port
->observed
= usbport_trig_port_observed(usbport_data
, usb_dev
,
204 len
= strlen(hub_name
) + 8;
205 port
->port_name
= kzalloc(len
, GFP_KERNEL
);
206 if (!port
->port_name
) {
210 snprintf(port
->port_name
, len
, "%s-port%d", hub_name
, portnum
);
212 sysfs_attr_init(&port
->attr
.attr
);
213 port
->attr
.attr
.name
= port
->port_name
;
214 port
->attr
.attr
.mode
= S_IRUSR
| S_IWUSR
;
215 port
->attr
.show
= usbport_trig_port_show
;
216 port
->attr
.store
= usbport_trig_port_store
;
218 err
= sysfs_add_file_to_group(&led_cdev
->dev
->kobj
, &port
->attr
.attr
,
221 goto err_free_port_name
;
223 list_add_tail(&port
->list
, &usbport_data
->ports
);
228 kfree(port
->port_name
);
235 static int usbport_trig_add_usb_dev_ports(struct usb_device
*usb_dev
,
238 struct usbport_trig_data
*usbport_data
= data
;
241 for (i
= 1; i
<= usb_dev
->maxchild
; i
++)
242 usbport_trig_add_port(usbport_data
, usb_dev
,
243 dev_name(&usb_dev
->dev
), i
);
248 static void usbport_trig_remove_port(struct usbport_trig_data
*usbport_data
,
249 struct usbport_trig_port
*port
)
251 struct led_classdev
*led_cdev
= usbport_data
->led_cdev
;
253 list_del(&port
->list
);
254 sysfs_remove_file_from_group(&led_cdev
->dev
->kobj
, &port
->attr
.attr
,
256 kfree(port
->port_name
);
260 static void usbport_trig_remove_usb_dev_ports(struct usbport_trig_data
*usbport_data
,
261 struct usb_device
*usb_dev
)
263 struct usbport_trig_port
*port
, *tmp
;
265 list_for_each_entry_safe(port
, tmp
, &usbport_data
->ports
, list
) {
266 if (port
->hub
== usb_dev
)
267 usbport_trig_remove_port(usbport_data
, port
);
271 /***************************************
273 ***************************************/
275 static int usbport_trig_notify(struct notifier_block
*nb
, unsigned long action
,
278 struct usbport_trig_data
*usbport_data
=
279 container_of(nb
, struct usbport_trig_data
, nb
);
280 struct led_classdev
*led_cdev
= usbport_data
->led_cdev
;
281 struct usb_device
*usb_dev
= data
;
284 observed
= usbport_trig_usb_dev_observed(usbport_data
, usb_dev
);
288 usbport_trig_add_usb_dev_ports(usb_dev
, usbport_data
);
289 if (observed
&& usbport_data
->count
++ == 0)
290 led_set_brightness(led_cdev
, LED_FULL
);
292 case USB_DEVICE_REMOVE
:
293 usbport_trig_remove_usb_dev_ports(usbport_data
, usb_dev
);
294 if (observed
&& --usbport_data
->count
== 0)
295 led_set_brightness(led_cdev
, LED_OFF
);
302 static int usbport_trig_activate(struct led_classdev
*led_cdev
)
304 struct usbport_trig_data
*usbport_data
;
307 usbport_data
= kzalloc(sizeof(*usbport_data
), GFP_KERNEL
);
310 usbport_data
->led_cdev
= led_cdev
;
313 INIT_LIST_HEAD(&usbport_data
->ports
);
314 err
= sysfs_create_group(&led_cdev
->dev
->kobj
, &ports_group
);
317 usb_for_each_dev(usbport_data
, usbport_trig_add_usb_dev_ports
);
318 usbport_trig_update_count(usbport_data
);
321 usbport_data
->nb
.notifier_call
= usbport_trig_notify
;
322 led_set_trigger_data(led_cdev
, usbport_data
);
323 usb_register_notify(&usbport_data
->nb
);
331 static void usbport_trig_deactivate(struct led_classdev
*led_cdev
)
333 struct usbport_trig_data
*usbport_data
= led_get_trigger_data(led_cdev
);
334 struct usbport_trig_port
*port
, *tmp
;
336 list_for_each_entry_safe(port
, tmp
, &usbport_data
->ports
, list
) {
337 usbport_trig_remove_port(usbport_data
, port
);
340 sysfs_remove_group(&led_cdev
->dev
->kobj
, &ports_group
);
342 usb_unregister_notify(&usbport_data
->nb
);
347 static struct led_trigger usbport_led_trigger
= {
349 .activate
= usbport_trig_activate
,
350 .deactivate
= usbport_trig_deactivate
,
353 static int __init
usbport_trig_init(void)
355 return led_trigger_register(&usbport_led_trigger
);
358 static void __exit
usbport_trig_exit(void)
360 led_trigger_unregister(&usbport_led_trigger
);
363 module_init(usbport_trig_init
);
364 module_exit(usbport_trig_exit
);
366 MODULE_AUTHOR("Rafał Miłecki <rafal@milecki.pl>");
367 MODULE_DESCRIPTION("USB port trigger");
368 MODULE_LICENSE("GPL v2");