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 static const struct attribute_group
*ports_groups
[] = {
127 /***************************************
128 * Adding & removing ports
129 ***************************************/
132 * usbport_trig_port_observed - Check if port should be observed
134 static bool usbport_trig_port_observed(struct usbport_trig_data
*usbport_data
,
135 struct usb_device
*usb_dev
, int port1
)
137 struct device
*dev
= usbport_data
->led_cdev
->dev
;
138 struct device_node
*led_np
= dev
->of_node
;
139 struct of_phandle_args args
;
140 struct device_node
*port_np
;
147 * Get node of port being added
149 * FIXME: This is really the device node of the connected device
151 port_np
= usb_of_get_device_node(usb_dev
, port1
);
155 of_node_put(port_np
);
157 /* Amount of trigger sources for this LED */
158 count
= of_count_phandle_with_args(led_np
, "trigger-sources",
159 "#trigger-source-cells");
161 dev_warn(dev
, "Failed to get trigger sources for %pOF\n",
166 /* Check list of sources for this specific port */
167 for (i
= 0; i
< count
; i
++) {
170 err
= of_parse_phandle_with_args(led_np
, "trigger-sources",
171 "#trigger-source-cells", i
,
174 dev_err(dev
, "Failed to get trigger source phandle at index %d: %d\n",
179 of_node_put(args
.np
);
181 if (args
.np
== port_np
)
188 static int usbport_trig_add_port(struct usbport_trig_data
*usbport_data
,
189 struct usb_device
*usb_dev
,
190 const char *hub_name
, int portnum
)
192 struct led_classdev
*led_cdev
= usbport_data
->led_cdev
;
193 struct usbport_trig_port
*port
;
197 port
= kzalloc(sizeof(*port
), GFP_KERNEL
);
203 port
->data
= usbport_data
;
205 port
->portnum
= portnum
;
206 port
->observed
= usbport_trig_port_observed(usbport_data
, usb_dev
,
209 len
= strlen(hub_name
) + 8;
210 port
->port_name
= kzalloc(len
, GFP_KERNEL
);
211 if (!port
->port_name
) {
215 snprintf(port
->port_name
, len
, "%s-port%d", hub_name
, portnum
);
217 sysfs_attr_init(&port
->attr
.attr
);
218 port
->attr
.attr
.name
= port
->port_name
;
219 port
->attr
.attr
.mode
= S_IRUSR
| S_IWUSR
;
220 port
->attr
.show
= usbport_trig_port_show
;
221 port
->attr
.store
= usbport_trig_port_store
;
223 err
= sysfs_add_file_to_group(&led_cdev
->dev
->kobj
, &port
->attr
.attr
,
226 goto err_free_port_name
;
228 list_add_tail(&port
->list
, &usbport_data
->ports
);
233 kfree(port
->port_name
);
240 static int usbport_trig_add_usb_dev_ports(struct usb_device
*usb_dev
,
243 struct usbport_trig_data
*usbport_data
= data
;
246 for (i
= 1; i
<= usb_dev
->maxchild
; i
++)
247 usbport_trig_add_port(usbport_data
, usb_dev
,
248 dev_name(&usb_dev
->dev
), i
);
253 static void usbport_trig_remove_port(struct usbport_trig_data
*usbport_data
,
254 struct usbport_trig_port
*port
)
256 struct led_classdev
*led_cdev
= usbport_data
->led_cdev
;
258 list_del(&port
->list
);
259 sysfs_remove_file_from_group(&led_cdev
->dev
->kobj
, &port
->attr
.attr
,
261 kfree(port
->port_name
);
265 static void usbport_trig_remove_usb_dev_ports(struct usbport_trig_data
*usbport_data
,
266 struct usb_device
*usb_dev
)
268 struct usbport_trig_port
*port
, *tmp
;
270 list_for_each_entry_safe(port
, tmp
, &usbport_data
->ports
, list
) {
271 if (port
->hub
== usb_dev
)
272 usbport_trig_remove_port(usbport_data
, port
);
276 /***************************************
278 ***************************************/
280 static int usbport_trig_notify(struct notifier_block
*nb
, unsigned long action
,
283 struct usbport_trig_data
*usbport_data
=
284 container_of(nb
, struct usbport_trig_data
, nb
);
285 struct led_classdev
*led_cdev
= usbport_data
->led_cdev
;
286 struct usb_device
*usb_dev
= data
;
289 observed
= usbport_trig_usb_dev_observed(usbport_data
, usb_dev
);
293 usbport_trig_add_usb_dev_ports(usb_dev
, usbport_data
);
294 if (observed
&& usbport_data
->count
++ == 0)
295 led_set_brightness(led_cdev
, LED_FULL
);
297 case USB_DEVICE_REMOVE
:
298 usbport_trig_remove_usb_dev_ports(usbport_data
, usb_dev
);
299 if (observed
&& --usbport_data
->count
== 0)
300 led_set_brightness(led_cdev
, LED_OFF
);
307 static int usbport_trig_activate(struct led_classdev
*led_cdev
)
309 struct usbport_trig_data
*usbport_data
;
311 usbport_data
= kzalloc(sizeof(*usbport_data
), GFP_KERNEL
);
314 usbport_data
->led_cdev
= led_cdev
;
317 INIT_LIST_HEAD(&usbport_data
->ports
);
318 usb_for_each_dev(usbport_data
, usbport_trig_add_usb_dev_ports
);
319 usbport_trig_update_count(usbport_data
);
322 usbport_data
->nb
.notifier_call
= usbport_trig_notify
;
323 led_set_trigger_data(led_cdev
, usbport_data
);
324 usb_register_notify(&usbport_data
->nb
);
329 static void usbport_trig_deactivate(struct led_classdev
*led_cdev
)
331 struct usbport_trig_data
*usbport_data
= led_get_trigger_data(led_cdev
);
332 struct usbport_trig_port
*port
, *tmp
;
334 list_for_each_entry_safe(port
, tmp
, &usbport_data
->ports
, list
) {
335 usbport_trig_remove_port(usbport_data
, port
);
338 usb_unregister_notify(&usbport_data
->nb
);
343 static struct led_trigger usbport_led_trigger
= {
345 .activate
= usbport_trig_activate
,
346 .deactivate
= usbport_trig_deactivate
,
347 .groups
= ports_groups
,
350 static int __init
usbport_trig_init(void)
352 return led_trigger_register(&usbport_led_trigger
);
355 static void __exit
usbport_trig_exit(void)
357 led_trigger_unregister(&usbport_led_trigger
);
360 module_init(usbport_trig_init
);
361 module_exit(usbport_trig_exit
);
363 MODULE_AUTHOR("Rafał Miłecki <rafal@milecki.pl>");
364 MODULE_DESCRIPTION("USB port trigger");
365 MODULE_LICENSE("GPL v2");