2 * LED USB device Trigger
4 * Toggles the LED to reflect the presence and activity of an USB device
5 * Copyright (C) Gabor Juhos <juhosg@openwrt.org>
7 * derived from ledtrig-netdev.c:
8 * Copyright 2007 Oliver Jowett <oliver@opencloud.com>
10 * ledtrig-netdev.c derived from ledtrig-timer.c:
11 * Copyright 2005-2006 Openedhand Ltd.
12 * Author: Richard Purdie <rpurdie@openedhand.com>
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License version 2 as
16 * published by the Free Software Foundation.
20 #include <linux/module.h>
21 #include <linux/jiffies.h>
22 #include <linux/kernel.h>
23 #include <linux/init.h>
24 #include <linux/list.h>
25 #include <linux/spinlock.h>
26 #include <linux/device.h>
27 #include <linux/sysdev.h>
28 #include <linux/timer.h>
29 #include <linux/ctype.h>
30 #include <linux/slab.h>
31 #include <linux/leds.h>
32 #include <linux/usb.h>
36 #define DEV_BUS_ID_SIZE 32
39 * Configurable sysfs attributes:
41 * device_name - name of the USB device to monitor
42 * activity_interval - duration of LED blink, in milliseconds
45 struct usbdev_trig_data
{
48 struct timer_list timer
;
49 struct notifier_block notifier
;
51 struct led_classdev
*led_cdev
;
52 struct usb_device
*usb_dev
;
54 char device_name
[DEV_BUS_ID_SIZE
];
59 static void usbdev_trig_update_state(struct usbdev_trig_data
*td
)
62 led_set_brightness(td
->led_cdev
, LED_FULL
);
64 led_set_brightness(td
->led_cdev
, LED_OFF
);
66 if (td
->interval
&& td
->usb_dev
)
67 mod_timer(&td
->timer
, jiffies
+ td
->interval
);
69 del_timer(&td
->timer
);
72 static ssize_t
usbdev_trig_name_show(struct device
*dev
,
73 struct device_attribute
*attr
,
76 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
77 struct usbdev_trig_data
*td
= led_cdev
->trigger_data
;
80 sprintf(buf
, "%s\n", td
->device_name
);
81 read_unlock(&td
->lock
);
83 return strlen(buf
) + 1;
86 static ssize_t
usbdev_trig_name_store(struct device
*dev
,
87 struct device_attribute
*attr
,
91 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
92 struct usbdev_trig_data
*td
= led_cdev
->trigger_data
;
94 if (size
< 0 || size
>= DEV_BUS_ID_SIZE
)
97 write_lock(&td
->lock
);
99 strcpy(td
->device_name
, buf
);
100 if (size
> 0 && td
->device_name
[size
- 1] == '\n')
101 td
->device_name
[size
- 1] = 0;
103 if (td
->device_name
[0] != 0) {
104 struct usb_device
*usb_dev
;
106 /* check for existing device to update from */
107 usb_dev
= usb_find_device_by_name(td
->device_name
);
110 usb_put_dev(td
->usb_dev
);
112 td
->usb_dev
= usb_dev
;
113 td
->last_urbnum
= atomic_read(&usb_dev
->urbnum
);
116 /* updates LEDs, may start timers */
117 usbdev_trig_update_state(td
);
120 write_unlock(&td
->lock
);
124 static DEVICE_ATTR(device_name
, 0644, usbdev_trig_name_show
,
125 usbdev_trig_name_store
);
127 static ssize_t
usbdev_trig_interval_show(struct device
*dev
,
128 struct device_attribute
*attr
,
131 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
132 struct usbdev_trig_data
*td
= led_cdev
->trigger_data
;
134 read_lock(&td
->lock
);
135 sprintf(buf
, "%u\n", jiffies_to_msecs(td
->interval
));
136 read_unlock(&td
->lock
);
138 return strlen(buf
) + 1;
141 static ssize_t
usbdev_trig_interval_store(struct device
*dev
,
142 struct device_attribute
*attr
,
146 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
147 struct usbdev_trig_data
*td
= led_cdev
->trigger_data
;
150 unsigned long value
= simple_strtoul(buf
, &after
, 10);
151 size_t count
= after
- buf
;
153 if (*after
&& isspace(*after
))
156 if (count
== size
&& value
<= 10000) {
157 write_lock(&td
->lock
);
158 td
->interval
= msecs_to_jiffies(value
);
159 usbdev_trig_update_state(td
); /* resets timer */
160 write_unlock(&td
->lock
);
167 static DEVICE_ATTR(activity_interval
, 0644, usbdev_trig_interval_show
,
168 usbdev_trig_interval_store
);
170 static int usbdev_trig_notify(struct notifier_block
*nb
,
174 struct usb_device
*usb_dev
;
175 struct usbdev_trig_data
*td
;
177 if (evt
!= USB_DEVICE_ADD
&& evt
!= USB_DEVICE_REMOVE
)
181 td
= container_of(nb
, struct usbdev_trig_data
, notifier
);
183 write_lock(&td
->lock
);
185 if (strcmp(dev_name(&usb_dev
->dev
), td
->device_name
))
188 if (evt
== USB_DEVICE_ADD
) {
189 usb_get_dev(usb_dev
);
190 if (td
->usb_dev
!= NULL
)
191 usb_put_dev(td
->usb_dev
);
192 td
->usb_dev
= usb_dev
;
193 td
->last_urbnum
= atomic_read(&usb_dev
->urbnum
);
194 } else if (evt
== USB_DEVICE_REMOVE
) {
195 if (td
->usb_dev
!= NULL
) {
196 usb_put_dev(td
->usb_dev
);
201 usbdev_trig_update_state(td
);
204 write_unlock(&td
->lock
);
208 /* here's the real work! */
209 static void usbdev_trig_timer(unsigned long arg
)
211 struct usbdev_trig_data
*td
= (struct usbdev_trig_data
*)arg
;
214 write_lock(&td
->lock
);
216 if (!td
->usb_dev
|| td
->interval
== 0) {
218 * we don't need to do timer work, just reflect device presence
221 led_set_brightness(td
->led_cdev
, LED_FULL
);
223 led_set_brightness(td
->led_cdev
, LED_OFF
);
229 new_urbnum
= atomic_read(&td
->usb_dev
->urbnum
);
235 * Base state is ON (device is present). If there's no device,
236 * we don't get this far and the LED is off.
238 * ON -> OFF on activity
240 if (td
->led_cdev
->brightness
== LED_OFF
)
241 led_set_brightness(td
->led_cdev
, LED_FULL
);
242 else if (td
->last_urbnum
!= new_urbnum
)
243 led_set_brightness(td
->led_cdev
, LED_OFF
);
248 * OFF -> ON on activity
250 if (td
->led_cdev
->brightness
== LED_FULL
)
251 led_set_brightness(td
->led_cdev
, LED_OFF
);
252 else if (td
->last_urbnum
!= new_urbnum
)
253 led_set_brightness(td
->led_cdev
, LED_FULL
);
256 td
->last_urbnum
= new_urbnum
;
257 mod_timer(&td
->timer
, jiffies
+ td
->interval
);
260 write_unlock(&td
->lock
);
263 static void usbdev_trig_activate(struct led_classdev
*led_cdev
)
265 struct usbdev_trig_data
*td
;
268 td
= kzalloc(sizeof(struct usbdev_trig_data
), GFP_KERNEL
);
272 rwlock_init(&td
->lock
);
274 td
->notifier
.notifier_call
= usbdev_trig_notify
;
275 td
->notifier
.priority
= 10;
277 setup_timer(&td
->timer
, usbdev_trig_timer
, (unsigned long) td
);
279 td
->led_cdev
= led_cdev
;
280 td
->interval
= msecs_to_jiffies(50);
282 led_cdev
->trigger_data
= td
;
284 rc
= device_create_file(led_cdev
->dev
, &dev_attr_device_name
);
288 rc
= device_create_file(led_cdev
->dev
, &dev_attr_activity_interval
);
290 goto err_out_device_name
;
292 usb_register_notify(&td
->notifier
);
296 device_remove_file(led_cdev
->dev
, &dev_attr_device_name
);
298 led_cdev
->trigger_data
= NULL
;
302 static void usbdev_trig_deactivate(struct led_classdev
*led_cdev
)
304 struct usbdev_trig_data
*td
= led_cdev
->trigger_data
;
307 usb_unregister_notify(&td
->notifier
);
309 device_remove_file(led_cdev
->dev
, &dev_attr_device_name
);
310 device_remove_file(led_cdev
->dev
, &dev_attr_activity_interval
);
312 write_lock(&td
->lock
);
315 usb_put_dev(td
->usb_dev
);
319 write_unlock(&td
->lock
);
321 del_timer_sync(&td
->timer
);
327 static struct led_trigger usbdev_led_trigger
= {
329 .activate
= usbdev_trig_activate
,
330 .deactivate
= usbdev_trig_deactivate
,
333 static int __init
usbdev_trig_init(void)
335 return led_trigger_register(&usbdev_led_trigger
);
338 static void __exit
usbdev_trig_exit(void)
340 led_trigger_unregister(&usbdev_led_trigger
);
343 module_init(usbdev_trig_init
);
344 module_exit(usbdev_trig_exit
);
346 MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
347 MODULE_DESCRIPTION("USB device LED trigger");
348 MODULE_LICENSE("GPL v2");