4 * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
11 #include <linux/device.h>
12 #include <linux/leds.h>
13 #include <linux/module.h>
14 #include <linux/slab.h>
15 #include <linux/usb.h>
17 struct usbport_trig_data
{
18 struct led_classdev
*led_cdev
;
19 struct list_head ports
;
20 struct notifier_block nb
;
21 int count
; /* Amount of connected matching devices */
24 struct usbport_trig_port
{
25 struct usbport_trig_data
*data
;
26 struct usb_device
*hub
;
30 struct device_attribute attr
;
31 struct list_head list
;
34 /***************************************
36 ***************************************/
39 * usbport_trig_usb_dev_observed - Check if dev is connected to observed port
41 static bool usbport_trig_usb_dev_observed(struct usbport_trig_data
*usbport_data
,
42 struct usb_device
*usb_dev
)
44 struct usbport_trig_port
*port
;
49 list_for_each_entry(port
, &usbport_data
->ports
, list
) {
50 if (usb_dev
->parent
== port
->hub
&&
51 usb_dev
->portnum
== port
->portnum
)
52 return port
->observed
;
58 static int usbport_trig_usb_dev_check(struct usb_device
*usb_dev
, void *data
)
60 struct usbport_trig_data
*usbport_data
= data
;
62 if (usbport_trig_usb_dev_observed(usbport_data
, usb_dev
))
63 usbport_data
->count
++;
69 * usbport_trig_update_count - Recalculate amount of connected matching devices
71 static void usbport_trig_update_count(struct usbport_trig_data
*usbport_data
)
73 struct led_classdev
*led_cdev
= usbport_data
->led_cdev
;
75 usbport_data
->count
= 0;
76 usb_for_each_dev(usbport_data
, usbport_trig_usb_dev_check
);
77 led_set_brightness(led_cdev
, usbport_data
->count
? LED_FULL
: LED_OFF
);
80 /***************************************
82 ***************************************/
84 static ssize_t
usbport_trig_port_show(struct device
*dev
,
85 struct device_attribute
*attr
, char *buf
)
87 struct usbport_trig_port
*port
= container_of(attr
,
88 struct usbport_trig_port
,
91 return sprintf(buf
, "%d\n", port
->observed
) + 1;
94 static ssize_t
usbport_trig_port_store(struct device
*dev
,
95 struct device_attribute
*attr
,
96 const char *buf
, size_t size
)
98 struct usbport_trig_port
*port
= container_of(attr
,
99 struct usbport_trig_port
,
102 if (!strcmp(buf
, "0") || !strcmp(buf
, "0\n"))
104 else if (!strcmp(buf
, "1") || !strcmp(buf
, "1\n"))
109 usbport_trig_update_count(port
->data
);
114 static struct attribute
*ports_attrs
[] = {
117 static const struct attribute_group ports_group
= {
119 .attrs
= ports_attrs
,
122 /***************************************
123 * Adding & removing ports
124 ***************************************/
126 static int usbport_trig_add_port(struct usbport_trig_data
*usbport_data
,
127 struct usb_device
*usb_dev
,
128 const char *hub_name
, int portnum
)
130 struct led_classdev
*led_cdev
= usbport_data
->led_cdev
;
131 struct usbport_trig_port
*port
;
135 port
= kzalloc(sizeof(*port
), GFP_KERNEL
);
141 port
->data
= usbport_data
;
143 port
->portnum
= portnum
;
145 len
= strlen(hub_name
) + 8;
146 port
->port_name
= kzalloc(len
, GFP_KERNEL
);
147 if (!port
->port_name
) {
151 snprintf(port
->port_name
, len
, "%s-port%d", hub_name
, portnum
);
153 port
->attr
.attr
.name
= port
->port_name
;
154 port
->attr
.attr
.mode
= S_IRUSR
| S_IWUSR
;
155 port
->attr
.show
= usbport_trig_port_show
;
156 port
->attr
.store
= usbport_trig_port_store
;
158 err
= sysfs_add_file_to_group(&led_cdev
->dev
->kobj
, &port
->attr
.attr
,
161 goto err_free_port_name
;
163 list_add_tail(&port
->list
, &usbport_data
->ports
);
168 kfree(port
->port_name
);
175 static int usbport_trig_add_usb_dev_ports(struct usb_device
*usb_dev
,
178 struct usbport_trig_data
*usbport_data
= data
;
181 for (i
= 1; i
<= usb_dev
->maxchild
; i
++)
182 usbport_trig_add_port(usbport_data
, usb_dev
,
183 dev_name(&usb_dev
->dev
), i
);
188 static void usbport_trig_remove_port(struct usbport_trig_data
*usbport_data
,
189 struct usbport_trig_port
*port
)
191 struct led_classdev
*led_cdev
= usbport_data
->led_cdev
;
193 list_del(&port
->list
);
194 sysfs_remove_file_from_group(&led_cdev
->dev
->kobj
, &port
->attr
.attr
,
196 kfree(port
->port_name
);
200 static void usbport_trig_remove_usb_dev_ports(struct usbport_trig_data
*usbport_data
,
201 struct usb_device
*usb_dev
)
203 struct usbport_trig_port
*port
, *tmp
;
205 list_for_each_entry_safe(port
, tmp
, &usbport_data
->ports
, list
) {
206 if (port
->hub
== usb_dev
)
207 usbport_trig_remove_port(usbport_data
, port
);
211 /***************************************
213 ***************************************/
215 static int usbport_trig_notify(struct notifier_block
*nb
, unsigned long action
,
218 struct usbport_trig_data
*usbport_data
=
219 container_of(nb
, struct usbport_trig_data
, nb
);
220 struct led_classdev
*led_cdev
= usbport_data
->led_cdev
;
221 struct usb_device
*usb_dev
= data
;
224 observed
= usbport_trig_usb_dev_observed(usbport_data
, usb_dev
);
228 usbport_trig_add_usb_dev_ports(usb_dev
, usbport_data
);
229 if (observed
&& usbport_data
->count
++ == 0)
230 led_set_brightness(led_cdev
, LED_FULL
);
232 case USB_DEVICE_REMOVE
:
233 usbport_trig_remove_usb_dev_ports(usbport_data
, usb_dev
);
234 if (observed
&& --usbport_data
->count
== 0)
235 led_set_brightness(led_cdev
, LED_OFF
);
242 static void usbport_trig_activate(struct led_classdev
*led_cdev
)
244 struct usbport_trig_data
*usbport_data
;
247 usbport_data
= kzalloc(sizeof(*usbport_data
), GFP_KERNEL
);
250 usbport_data
->led_cdev
= led_cdev
;
253 INIT_LIST_HEAD(&usbport_data
->ports
);
254 err
= sysfs_create_group(&led_cdev
->dev
->kobj
, &ports_group
);
257 usb_for_each_dev(usbport_data
, usbport_trig_add_usb_dev_ports
);
260 usbport_data
->nb
.notifier_call
= usbport_trig_notify
,
261 led_cdev
->trigger_data
= usbport_data
;
262 usb_register_notify(&usbport_data
->nb
);
264 led_cdev
->activated
= true;
271 static void usbport_trig_deactivate(struct led_classdev
*led_cdev
)
273 struct usbport_trig_data
*usbport_data
= led_cdev
->trigger_data
;
274 struct usbport_trig_port
*port
, *tmp
;
276 if (!led_cdev
->activated
)
279 list_for_each_entry_safe(port
, tmp
, &usbport_data
->ports
, list
) {
280 usbport_trig_remove_port(usbport_data
, port
);
283 usb_unregister_notify(&usbport_data
->nb
);
285 sysfs_remove_group(&led_cdev
->dev
->kobj
, &ports_group
);
289 led_cdev
->activated
= false;
292 static struct led_trigger usbport_led_trigger
= {
294 .activate
= usbport_trig_activate
,
295 .deactivate
= usbport_trig_deactivate
,
298 static int __init
usbport_trig_init(void)
300 return led_trigger_register(&usbport_led_trigger
);
303 static void __exit
usbport_trig_exit(void)
305 led_trigger_unregister(&usbport_led_trigger
);
308 module_init(usbport_trig_init
);
309 module_exit(usbport_trig_exit
);
311 MODULE_AUTHOR("Rafał Miłecki <rafal@milecki.pl>");
312 MODULE_DESCRIPTION("USB port trigger");
313 MODULE_LICENSE("GPL v2");