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 struct usbdev_trig_match
{
88 struct usb_device
*usb_dev
;
91 static int usbdev_trig_find_usb_dev(struct usb_device
*usb_dev
, void *data
)
93 struct usbdev_trig_match
*match
= data
;
95 if (strcmp(dev_name(&usb_dev
->dev
), match
->device_name
) != 0)
98 if (WARN_ON(match
->usb_dev
))
101 dev_dbg(&usb_dev
->dev
, "matched this device!\n");
102 match
->usb_dev
= usb_get_dev(usb_dev
);
107 static ssize_t
usbdev_trig_name_store(struct device
*dev
,
108 struct device_attribute
*attr
,
112 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
113 struct usbdev_trig_data
*td
= led_cdev
->trigger_data
;
115 if (size
< 0 || size
>= DEV_BUS_ID_SIZE
)
118 write_lock(&td
->lock
);
120 strcpy(td
->device_name
, buf
);
121 if (size
> 0 && td
->device_name
[size
- 1] == '\n')
122 td
->device_name
[size
- 1] = 0;
124 if (td
->device_name
[0] != 0) {
125 struct usbdev_trig_match match
= {
126 .device_name
= td
->device_name
,
129 /* check for existing device to update from */
130 usb_for_each_dev(&match
, usbdev_trig_find_usb_dev
);
133 usb_put_dev(td
->usb_dev
);
135 td
->usb_dev
= match
.usb_dev
;
136 td
->last_urbnum
= atomic_read(&match
.usb_dev
->urbnum
);
139 /* updates LEDs, may start timers */
140 usbdev_trig_update_state(td
);
143 write_unlock(&td
->lock
);
147 static DEVICE_ATTR(device_name
, 0644, usbdev_trig_name_show
,
148 usbdev_trig_name_store
);
150 static ssize_t
usbdev_trig_interval_show(struct device
*dev
,
151 struct device_attribute
*attr
,
154 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
155 struct usbdev_trig_data
*td
= led_cdev
->trigger_data
;
157 read_lock(&td
->lock
);
158 sprintf(buf
, "%u\n", jiffies_to_msecs(td
->interval
));
159 read_unlock(&td
->lock
);
161 return strlen(buf
) + 1;
164 static ssize_t
usbdev_trig_interval_store(struct device
*dev
,
165 struct device_attribute
*attr
,
169 struct led_classdev
*led_cdev
= dev_get_drvdata(dev
);
170 struct usbdev_trig_data
*td
= led_cdev
->trigger_data
;
173 unsigned long value
= simple_strtoul(buf
, &after
, 10);
174 size_t count
= after
- buf
;
176 if (*after
&& isspace(*after
))
179 if (count
== size
&& value
<= 10000) {
180 write_lock(&td
->lock
);
181 td
->interval
= msecs_to_jiffies(value
);
182 usbdev_trig_update_state(td
); /* resets timer */
183 write_unlock(&td
->lock
);
190 static DEVICE_ATTR(activity_interval
, 0644, usbdev_trig_interval_show
,
191 usbdev_trig_interval_store
);
193 static int usbdev_trig_notify(struct notifier_block
*nb
,
197 struct usb_device
*usb_dev
;
198 struct usbdev_trig_data
*td
;
200 if (evt
!= USB_DEVICE_ADD
&& evt
!= USB_DEVICE_REMOVE
)
204 td
= container_of(nb
, struct usbdev_trig_data
, notifier
);
206 write_lock(&td
->lock
);
208 if (strcmp(dev_name(&usb_dev
->dev
), td
->device_name
))
211 if (evt
== USB_DEVICE_ADD
) {
212 usb_get_dev(usb_dev
);
213 if (td
->usb_dev
!= NULL
)
214 usb_put_dev(td
->usb_dev
);
215 td
->usb_dev
= usb_dev
;
216 td
->last_urbnum
= atomic_read(&usb_dev
->urbnum
);
217 } else if (evt
== USB_DEVICE_REMOVE
) {
218 if (td
->usb_dev
!= NULL
) {
219 usb_put_dev(td
->usb_dev
);
224 usbdev_trig_update_state(td
);
227 write_unlock(&td
->lock
);
231 /* here's the real work! */
232 static void usbdev_trig_timer(unsigned long arg
)
234 struct usbdev_trig_data
*td
= (struct usbdev_trig_data
*)arg
;
237 write_lock(&td
->lock
);
239 if (!td
->usb_dev
|| td
->interval
== 0) {
241 * we don't need to do timer work, just reflect device presence
244 led_set_brightness(td
->led_cdev
, LED_FULL
);
246 led_set_brightness(td
->led_cdev
, LED_OFF
);
252 new_urbnum
= atomic_read(&td
->usb_dev
->urbnum
);
258 * Base state is ON (device is present). If there's no device,
259 * we don't get this far and the LED is off.
261 * ON -> OFF on activity
263 if (td
->led_cdev
->brightness
== LED_OFF
)
264 led_set_brightness(td
->led_cdev
, LED_FULL
);
265 else if (td
->last_urbnum
!= new_urbnum
)
266 led_set_brightness(td
->led_cdev
, LED_OFF
);
271 * OFF -> ON on activity
273 if (td
->led_cdev
->brightness
== LED_FULL
)
274 led_set_brightness(td
->led_cdev
, LED_OFF
);
275 else if (td
->last_urbnum
!= new_urbnum
)
276 led_set_brightness(td
->led_cdev
, LED_FULL
);
279 td
->last_urbnum
= new_urbnum
;
280 mod_timer(&td
->timer
, jiffies
+ td
->interval
);
283 write_unlock(&td
->lock
);
286 static void usbdev_trig_activate(struct led_classdev
*led_cdev
)
288 struct usbdev_trig_data
*td
;
291 td
= kzalloc(sizeof(struct usbdev_trig_data
), GFP_KERNEL
);
295 rwlock_init(&td
->lock
);
297 td
->notifier
.notifier_call
= usbdev_trig_notify
;
298 td
->notifier
.priority
= 10;
300 setup_timer(&td
->timer
, usbdev_trig_timer
, (unsigned long) td
);
302 td
->led_cdev
= led_cdev
;
303 td
->interval
= msecs_to_jiffies(50);
305 led_cdev
->trigger_data
= td
;
307 rc
= device_create_file(led_cdev
->dev
, &dev_attr_device_name
);
311 rc
= device_create_file(led_cdev
->dev
, &dev_attr_activity_interval
);
313 goto err_out_device_name
;
315 usb_register_notify(&td
->notifier
);
319 device_remove_file(led_cdev
->dev
, &dev_attr_device_name
);
321 led_cdev
->trigger_data
= NULL
;
325 static void usbdev_trig_deactivate(struct led_classdev
*led_cdev
)
327 struct usbdev_trig_data
*td
= led_cdev
->trigger_data
;
330 usb_unregister_notify(&td
->notifier
);
332 device_remove_file(led_cdev
->dev
, &dev_attr_device_name
);
333 device_remove_file(led_cdev
->dev
, &dev_attr_activity_interval
);
335 write_lock(&td
->lock
);
338 usb_put_dev(td
->usb_dev
);
342 write_unlock(&td
->lock
);
344 del_timer_sync(&td
->timer
);
350 static struct led_trigger usbdev_led_trigger
= {
352 .activate
= usbdev_trig_activate
,
353 .deactivate
= usbdev_trig_deactivate
,
356 static int __init
usbdev_trig_init(void)
358 return led_trigger_register(&usbdev_led_trigger
);
361 static void __exit
usbdev_trig_exit(void)
363 led_trigger_unregister(&usbdev_led_trigger
);
366 module_init(usbdev_trig_init
);
367 module_exit(usbdev_trig_exit
);
369 MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
370 MODULE_DESCRIPTION("USB device LED trigger");
371 MODULE_LICENSE("GPL v2");