From: Gabor Juhos Date: Fri, 17 Dec 2010 17:10:11 +0000 (+0000) Subject: generic: add LED trigger for USB device presence/activity X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=a059a11ee1e03c0cbe1dd400328b2d71820848d2;p=lede.git generic: add LED trigger for USB device presence/activity SVN-Revision: 24646 --- diff --git a/package/kernel/modules/other.mk b/package/kernel/modules/other.mk index 97869747bc..553466d7bf 100644 --- a/package/kernel/modules/other.mk +++ b/package/kernel/modules/other.mk @@ -573,6 +573,21 @@ endef $(eval $(call KernelPackage,ledtrig-netfilter)) +define KernelPackage/ledtrig-usbdev + SUBMENU:=$(OTHER_MENU) + TITLE:=LED USB device Trigger + DEPENDS:=@USB_SUPPORT +kmod-usb-core + KCONFIG:=CONFIG_LEDS_TRIGGER_USBDEV + FILES:=$(LINUX_DIR)/drivers/leds/ledtrig-usbdev.ko + AUTOLOAD:=$(call AutoLoad,50,ledtrig-usbdev) +endef + +define KernelPackage/ledtrig-usbdev/description + Kernel module to drive LEDs based on USB device presence/activity. +endef + +$(eval $(call KernelPackage,ledtrig-usbdev)) + define KernelPackage/lp SUBMENU:=$(OTHER_MENU) diff --git a/target/linux/generic/config-2.6.32 b/target/linux/generic/config-2.6.32 index da93d605da..7e8cff0d08 100644 --- a/target/linux/generic/config-2.6.32 +++ b/target/linux/generic/config-2.6.32 @@ -1157,6 +1157,7 @@ CONFIG_LEDS_TRIGGER_HEARTBEAT=y # CONFIG_LEDS_TRIGGER_MORSE is not set CONFIG_LEDS_TRIGGER_NETDEV=y CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_USBDEV is not set # CONFIG_LEGACY_PTYS is not set # CONFIG_LIB80211 is not set # CONFIG_LIB80211_CRYPT_CCMP is not set diff --git a/target/linux/generic/config-2.6.36 b/target/linux/generic/config-2.6.36 index a44364e09c..aa48fc1fee 100644 --- a/target/linux/generic/config-2.6.36 +++ b/target/linux/generic/config-2.6.36 @@ -1164,6 +1164,7 @@ CONFIG_LEDS_TRIGGER_HEARTBEAT=y # CONFIG_LEDS_TRIGGER_MORSE is not set CONFIG_LEDS_TRIGGER_NETDEV=y CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_USBDEV is not set # CONFIG_LEGACY_PTYS is not set # CONFIG_LIB80211 is not set # CONFIG_LIB80211_CRYPT_CCMP is not set diff --git a/target/linux/generic/config-2.6.37 b/target/linux/generic/config-2.6.37 index e0b2845992..cbb1832fab 100644 --- a/target/linux/generic/config-2.6.37 +++ b/target/linux/generic/config-2.6.37 @@ -1174,6 +1174,7 @@ CONFIG_LEDS_TRIGGER_HEARTBEAT=y # CONFIG_LEDS_TRIGGER_MORSE is not set CONFIG_LEDS_TRIGGER_NETDEV=y CONFIG_LEDS_TRIGGER_TIMER=y +# CONFIG_LEDS_TRIGGER_USBDEV is not set # CONFIG_LEGACY_PTYS is not set # CONFIG_LIB80211 is not set # CONFIG_LIB80211_CRYPT_CCMP is not set diff --git a/target/linux/generic/files/drivers/leds/ledtrig-usbdev.c b/target/linux/generic/files/drivers/leds/ledtrig-usbdev.c new file mode 100644 index 0000000000..70b0e392ad --- /dev/null +++ b/target/linux/generic/files/drivers/leds/ledtrig-usbdev.c @@ -0,0 +1,348 @@ +/* + * LED USB device Trigger + * + * Toggles the LED to reflect the presence and activity of an USB device + * Copyright (C) Gabor Juhos + * + * derived from ledtrig-netdev.c: + * Copyright 2007 Oliver Jowett + * + * ledtrig-netdev.c derived from ledtrig-timer.c: + * Copyright 2005-2006 Openedhand Ltd. + * Author: Richard Purdie + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "leds.h" + +#define DEV_BUS_ID_SIZE 32 + +/* + * Configurable sysfs attributes: + * + * device_name - name of the USB device to monitor + * activity_interval - duration of LED blink, in milliseconds + */ + +struct usbdev_trig_data { + rwlock_t lock; + + struct timer_list timer; + struct notifier_block notifier; + + struct led_classdev *led_cdev; + struct usb_device *usb_dev; + + char device_name[DEV_BUS_ID_SIZE]; + unsigned interval; + int last_urbnum; +}; + +static void usbdev_trig_update_state(struct usbdev_trig_data *td) +{ + if (td->usb_dev) + led_set_brightness(td->led_cdev, LED_FULL); + else + led_set_brightness(td->led_cdev, LED_OFF); + + if (td->interval && td->usb_dev) + mod_timer(&td->timer, jiffies + td->interval); + else + del_timer(&td->timer); +} + +static ssize_t usbdev_trig_name_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct usbdev_trig_data *td = led_cdev->trigger_data; + + read_lock(&td->lock); + sprintf(buf, "%s\n", td->device_name); + read_unlock(&td->lock); + + return strlen(buf) + 1; +} + +static ssize_t usbdev_trig_name_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct usbdev_trig_data *td = led_cdev->trigger_data; + + if (size < 0 || size >= DEV_BUS_ID_SIZE) + return -EINVAL; + + write_lock(&td->lock); + + strcpy(td->device_name, buf); + if (size > 0 && td->device_name[size - 1] == '\n') + td->device_name[size - 1] = 0; + + if (td->device_name[0] != 0) { + struct usb_device *usb_dev; + + /* check for existing device to update from */ + usb_dev = usb_find_device_by_name(td->device_name); + if (usb_dev) { + if (td->usb_dev) + usb_put_dev(td->usb_dev); + + td->usb_dev = usb_dev; + td->last_urbnum = atomic_read(&usb_dev->urbnum); + } + + /* updates LEDs, may start timers */ + usbdev_trig_update_state(td); + } + + write_unlock(&td->lock); + return size; +} + +static DEVICE_ATTR(device_name, 0644, usbdev_trig_name_show, + usbdev_trig_name_store); + +static ssize_t usbdev_trig_interval_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct usbdev_trig_data *td = led_cdev->trigger_data; + + read_lock(&td->lock); + sprintf(buf, "%u\n", jiffies_to_msecs(td->interval)); + read_unlock(&td->lock); + + return strlen(buf) + 1; +} + +static ssize_t usbdev_trig_interval_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct usbdev_trig_data *td = led_cdev->trigger_data; + int ret = -EINVAL; + char *after; + unsigned long value = simple_strtoul(buf, &after, 10); + size_t count = after - buf; + + if (*after && isspace(*after)) + count++; + + if (count == size && value <= 10000) { + write_lock(&td->lock); + td->interval = msecs_to_jiffies(value); + usbdev_trig_update_state(td); /* resets timer */ + write_unlock(&td->lock); + ret = count; + } + + return ret; +} + +static DEVICE_ATTR(activity_interval, 0644, usbdev_trig_interval_show, + usbdev_trig_interval_store); + +static int usbdev_trig_notify(struct notifier_block *nb, + unsigned long evt, + void *data) +{ + struct usb_device *usb_dev; + struct usbdev_trig_data *td; + + if (evt != USB_DEVICE_ADD && evt != USB_DEVICE_REMOVE) + return NOTIFY_DONE; + + usb_dev = data; + td = container_of(nb, struct usbdev_trig_data, notifier); + + write_lock(&td->lock); + + if (strcmp(dev_name(&usb_dev->dev), td->device_name)) + goto done; + + if (evt == USB_DEVICE_ADD) { + usb_get_dev(usb_dev); + if (td->usb_dev != NULL) + usb_put_dev(td->usb_dev); + td->usb_dev = usb_dev; + td->last_urbnum = atomic_read(&usb_dev->urbnum); + } else if (evt == USB_DEVICE_REMOVE) { + if (td->usb_dev != NULL) { + usb_put_dev(td->usb_dev); + td->usb_dev = NULL; + } + } + + usbdev_trig_update_state(td); + +done: + write_unlock(&td->lock); + return NOTIFY_DONE; +} + +/* here's the real work! */ +static void usbdev_trig_timer(unsigned long arg) +{ + struct usbdev_trig_data *td = (struct usbdev_trig_data *)arg; + int new_urbnum; + + write_lock(&td->lock); + + if (!td->usb_dev || td->interval == 0) { + /* + * we don't need to do timer work, just reflect device presence + */ + if (td->usb_dev) + led_set_brightness(td->led_cdev, LED_FULL); + else + led_set_brightness(td->led_cdev, LED_OFF); + + goto no_restart; + } + + if (td->interval) + new_urbnum = atomic_read(&td->usb_dev->urbnum); + else + new_urbnum = 0; + + if (td->usb_dev) { + /* + * Base state is ON (device is present). If there's no device, + * we don't get this far and the LED is off. + * OFF -> ON always + * ON -> OFF on activity + */ + if (td->led_cdev->brightness == LED_OFF) + led_set_brightness(td->led_cdev, LED_FULL); + else if (td->last_urbnum != new_urbnum) + led_set_brightness(td->led_cdev, LED_OFF); + } else { + /* + * base state is OFF + * ON -> OFF always + * OFF -> ON on activity + */ + if (td->led_cdev->brightness == LED_FULL) + led_set_brightness(td->led_cdev, LED_OFF); + else if (td->last_urbnum != new_urbnum) + led_set_brightness(td->led_cdev, LED_FULL); + } + + td->last_urbnum = new_urbnum; + mod_timer(&td->timer, jiffies + td->interval); + +no_restart: + write_unlock(&td->lock); +} + +static void usbdev_trig_activate(struct led_classdev *led_cdev) +{ + struct usbdev_trig_data *td; + int rc; + + td = kzalloc(sizeof(struct usbdev_trig_data), GFP_KERNEL); + if (!td) + return; + + rwlock_init(&td->lock); + + td->notifier.notifier_call = usbdev_trig_notify; + td->notifier.priority = 10; + + setup_timer(&td->timer, usbdev_trig_timer, (unsigned long) td); + + td->led_cdev = led_cdev; + td->interval = msecs_to_jiffies(50); + + led_cdev->trigger_data = td; + + rc = device_create_file(led_cdev->dev, &dev_attr_device_name); + if (rc) + goto err_out; + + rc = device_create_file(led_cdev->dev, &dev_attr_activity_interval); + if (rc) + goto err_out_device_name; + + usb_register_notify(&td->notifier); + return; + +err_out_device_name: + device_remove_file(led_cdev->dev, &dev_attr_device_name); +err_out: + led_cdev->trigger_data = NULL; + kfree(td); +} + +static void usbdev_trig_deactivate(struct led_classdev *led_cdev) +{ + struct usbdev_trig_data *td = led_cdev->trigger_data; + + if (td) { + usb_unregister_notify(&td->notifier); + + device_remove_file(led_cdev->dev, &dev_attr_device_name); + device_remove_file(led_cdev->dev, &dev_attr_activity_interval); + + write_lock(&td->lock); + + if (td->usb_dev) { + usb_put_dev(td->usb_dev); + td->usb_dev = NULL; + } + + write_unlock(&td->lock); + + del_timer_sync(&td->timer); + + kfree(td); + } +} + +static struct led_trigger usbdev_led_trigger = { + .name = "usbdev", + .activate = usbdev_trig_activate, + .deactivate = usbdev_trig_deactivate, +}; + +static int __init usbdev_trig_init(void) +{ + return led_trigger_register(&usbdev_led_trigger); +} + +static void __exit usbdev_trig_exit(void) +{ + led_trigger_unregister(&usbdev_led_trigger); +} + +module_init(usbdev_trig_init); +module_exit(usbdev_trig_exit); + +MODULE_AUTHOR("Gabor Juhos "); +MODULE_DESCRIPTION("USB device LED trigger"); +MODULE_LICENSE("GPL v2"); diff --git a/target/linux/generic/patches-2.6.32/403-ledtrig-usbdev.patch b/target/linux/generic/patches-2.6.32/403-ledtrig-usbdev.patch new file mode 100644 index 0000000000..8d208b1387 --- /dev/null +++ b/target/linux/generic/patches-2.6.32/403-ledtrig-usbdev.patch @@ -0,0 +1,21 @@ +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -326,4 +326,11 @@ config LEDS_TRIGGER_NETDEV + This allows LEDs to be controlled by network device activity. + If unsure, say Y. + ++config LEDS_TRIGGER_USBDEV ++ tristate "LED USB device Trigger" ++ depends on USB && LEDS_TRIGGERS ++ help ++ This allows LEDs to be controlled by the presence/activity of ++ an USB device. If unsure, say N. ++ + endif # NEW_LEDS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -44,3 +44,4 @@ obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledt + obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o + obj-$(CONFIG_LEDS_TRIGGER_MORSE) += ledtrig-morse.o + obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o ++obj-$(CONFIG_LEDS_TRIGGER_USBDEV) += ledtrig-usbdev.o diff --git a/target/linux/generic/patches-2.6.34/403-ledtrig-usbdev.patch b/target/linux/generic/patches-2.6.34/403-ledtrig-usbdev.patch new file mode 100644 index 0000000000..2fe8121e4b --- /dev/null +++ b/target/linux/generic/patches-2.6.34/403-ledtrig-usbdev.patch @@ -0,0 +1,21 @@ +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -365,4 +365,11 @@ config LEDS_TRIGGER_NETDEV + This allows LEDs to be controlled by network device activity. + If unsure, say Y. + ++config LEDS_TRIGGER_USBDEV ++ tristate "LED USB device Trigger" ++ depends on USB && LEDS_TRIGGERS ++ help ++ This allows LEDs to be controlled by the presence/activity of ++ an USB device. If unsure, say N. ++ + endif # NEW_LEDS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -48,3 +48,4 @@ obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledt + obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o + obj-$(CONFIG_LEDS_TRIGGER_MORSE) += ledtrig-morse.o + obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o ++obj-$(CONFIG_LEDS_TRIGGER_USBDEV) += ledtrig-usbdev.o diff --git a/target/linux/generic/patches-2.6.35/403-ledtrig-usbdev.patch b/target/linux/generic/patches-2.6.35/403-ledtrig-usbdev.patch new file mode 100644 index 0000000000..a84ce847dd --- /dev/null +++ b/target/linux/generic/patches-2.6.35/403-ledtrig-usbdev.patch @@ -0,0 +1,21 @@ +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -382,4 +382,11 @@ config LEDS_TRIGGER_NETDEV + This allows LEDs to be controlled by network device activity. + If unsure, say Y. + ++config LEDS_TRIGGER_USBDEV ++ tristate "LED USB device Trigger" ++ depends on USB && LEDS_TRIGGERS ++ help ++ This allows LEDs to be controlled by the presence/activity of ++ an USB device. If unsure, say N. ++ + endif # NEW_LEDS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -50,3 +50,4 @@ obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledt + obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o + obj-$(CONFIG_LEDS_TRIGGER_MORSE) += ledtrig-morse.o + obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o ++obj-$(CONFIG_LEDS_TRIGGER_USBDEV) += ledtrig-usbdev.o diff --git a/target/linux/generic/patches-2.6.36/403-ledtrig-usbdev.patch b/target/linux/generic/patches-2.6.36/403-ledtrig-usbdev.patch new file mode 100644 index 0000000000..fe334d5ed1 --- /dev/null +++ b/target/linux/generic/patches-2.6.36/403-ledtrig-usbdev.patch @@ -0,0 +1,21 @@ +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -391,4 +391,11 @@ config LEDS_TRIGGER_NETDEV + This allows LEDs to be controlled by network device activity. + If unsure, say Y. + ++config LEDS_TRIGGER_USBDEV ++ tristate "LED USB device Trigger" ++ depends on USB && LEDS_TRIGGERS ++ help ++ This allows LEDs to be controlled by the presence/activity of ++ an USB device. If unsure, say N. ++ + endif # NEW_LEDS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -51,3 +51,4 @@ obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledt + obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o + obj-$(CONFIG_LEDS_TRIGGER_MORSE) += ledtrig-morse.o + obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o ++obj-$(CONFIG_LEDS_TRIGGER_USBDEV) += ledtrig-usbdev.o diff --git a/target/linux/generic/patches-2.6.37/403-ledtrig-usbdev.patch b/target/linux/generic/patches-2.6.37/403-ledtrig-usbdev.patch new file mode 100644 index 0000000000..fb9bcfa697 --- /dev/null +++ b/target/linux/generic/patches-2.6.37/403-ledtrig-usbdev.patch @@ -0,0 +1,21 @@ +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -450,4 +450,11 @@ config LEDS_TRIGGER_NETDEV + This allows LEDs to be controlled by network device activity. + If unsure, say Y. + ++config LEDS_TRIGGER_USBDEV ++ tristate "LED USB device Trigger" ++ depends on USB && LEDS_TRIGGERS ++ help ++ This allows LEDs to be controlled by the presence/activity of ++ an USB device. If unsure, say N. ++ + endif # NEW_LEDS +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -54,3 +54,4 @@ obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledt + obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o + obj-$(CONFIG_LEDS_TRIGGER_MORSE) += ledtrig-morse.o + obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o ++obj-$(CONFIG_LEDS_TRIGGER_USBDEV) += ledtrig-usbdev.o