mirror of
				https://github.com/Ysurac/openmptcprouter.git
				synced 2025-03-09 15:40:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			235 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			235 lines
		
	
	
	
		
			6.1 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 7dadb839154d47ae90fd3b6d3eab825a11bb1f21 Mon Sep 17 00:00:00 2001
 | |
| From: Phil Elwell <phil@raspberrypi.com>
 | |
| Date: Mon, 13 Jul 2020 10:33:19 +0100
 | |
| Subject: [PATCH] leds: Add the actpwr trigger
 | |
| 
 | |
| The actpwr trigger is a meta trigger that cycles between an inverted
 | |
| mmc0 and default-on. It is written in a way that could fairly easily
 | |
| be generalised to support alternative sets of source triggers.
 | |
| 
 | |
| Signed-off-by: Phil Elwell <phil@raspberrypi.com>
 | |
| ---
 | |
|  drivers/leds/trigger/Kconfig          |  11 ++
 | |
|  drivers/leds/trigger/Makefile         |   1 +
 | |
|  drivers/leds/trigger/ledtrig-actpwr.c | 190 ++++++++++++++++++++++++++
 | |
|  3 files changed, 202 insertions(+)
 | |
|  create mode 100644 drivers/leds/trigger/ledtrig-actpwr.c
 | |
| 
 | |
| --- a/drivers/leds/trigger/Kconfig
 | |
| +++ b/drivers/leds/trigger/Kconfig
 | |
| @@ -161,4 +161,15 @@ config LEDS_TRIGGER_TTY
 | |
|  
 | |
|  	  When build as a module this driver will be called ledtrig-tty.
 | |
|  
 | |
| +config LEDS_TRIGGER_ACTPWR
 | |
| +	tristate "ACT/PWR Input Trigger"
 | |
| +	depends on LEDS_TRIGGERS
 | |
| +	help
 | |
| +	  This trigger is intended for platforms that have one software-
 | |
| +	  controllable LED and no dedicated activity or power LEDs, hence the
 | |
| +	  need to make the one LED perform both functions. It cycles between
 | |
| +	  default-on and an inverted mmc0 every 500ms, guaranteeing that it is
 | |
| +	  on for at least half of the time.
 | |
| +	  If unsure, say N.
 | |
| +
 | |
|  endif # LEDS_TRIGGERS
 | |
| --- a/drivers/leds/trigger/Makefile
 | |
| +++ b/drivers/leds/trigger/Makefile
 | |
| @@ -17,3 +17,4 @@ obj-$(CONFIG_LEDS_TRIGGER_NETDEV)	+= led
 | |
|  obj-$(CONFIG_LEDS_TRIGGER_PATTERN)	+= ledtrig-pattern.o
 | |
|  obj-$(CONFIG_LEDS_TRIGGER_AUDIO)	+= ledtrig-audio.o
 | |
|  obj-$(CONFIG_LEDS_TRIGGER_TTY)		+= ledtrig-tty.o
 | |
| +obj-$(CONFIG_LEDS_TRIGGER_ACTPWR)	+= ledtrig-actpwr.o
 | |
| --- /dev/null
 | |
| +++ b/drivers/leds/trigger/ledtrig-actpwr.c
 | |
| @@ -0,0 +1,190 @@
 | |
| +// SPDX-License-Identifier: GPL-2.0
 | |
| +/*
 | |
| + * Activity/power trigger
 | |
| + *
 | |
| + * Copyright (C) 2020 Raspberry Pi (Trading) Ltd.
 | |
| + *
 | |
| + * Based on Atsushi Nemoto's ledtrig-heartbeat.c, although there may be
 | |
| + * nothing left of the original now.
 | |
| + */
 | |
| +
 | |
| +#include <linux/module.h>
 | |
| +#include <linux/kernel.h>
 | |
| +#include <linux/init.h>
 | |
| +#include <linux/timer.h>
 | |
| +#include <linux/leds.h>
 | |
| +#include "../leds.h"
 | |
| +
 | |
| +enum {
 | |
| +	TRIG_ACT,
 | |
| +	TRIG_PWR,
 | |
| +
 | |
| +	TRIG_COUNT
 | |
| +};
 | |
| +
 | |
| +struct actpwr_trig_src {
 | |
| +	const char *name;
 | |
| +	int interval;
 | |
| +	bool invert;
 | |
| +};
 | |
| +
 | |
| +struct actpwr_vled {
 | |
| +	struct led_classdev cdev;
 | |
| +	struct actpwr_trig_data *parent;
 | |
| +	enum led_brightness value;
 | |
| +	unsigned int interval;
 | |
| +	bool invert;
 | |
| +};
 | |
| +
 | |
| +struct actpwr_trig_data {
 | |
| +	struct led_trigger trig;
 | |
| +	struct actpwr_vled virt_leds[TRIG_COUNT];
 | |
| +	struct actpwr_vled *active;
 | |
| +	struct timer_list timer;
 | |
| +	int next_active;
 | |
| +};
 | |
| +
 | |
| +static int actpwr_trig_activate(struct led_classdev *led_cdev);
 | |
| +static void actpwr_trig_deactivate(struct led_classdev *led_cdev);
 | |
| +
 | |
| +static const struct actpwr_trig_src actpwr_trig_sources[TRIG_COUNT] = {
 | |
| +	[TRIG_ACT] = { "mmc0", 500, true },
 | |
| +	[TRIG_PWR] = { "default-on", 500, false },
 | |
| +};
 | |
| +
 | |
| +static struct actpwr_trig_data actpwr_data = {
 | |
| +	{
 | |
| +		.name     = "actpwr",
 | |
| +		.activate = actpwr_trig_activate,
 | |
| +		.deactivate = actpwr_trig_deactivate,
 | |
| +	}
 | |
| +};
 | |
| +
 | |
| +static void actpwr_brightness_set(struct led_classdev *led_cdev,
 | |
| +				  enum led_brightness value)
 | |
| +{
 | |
| +	struct actpwr_vled *vled = container_of(led_cdev, struct actpwr_vled,
 | |
| +					       cdev);
 | |
| +	struct actpwr_trig_data *trig = vled->parent;
 | |
| +
 | |
| +	if (vled->invert)
 | |
| +		value = !value;
 | |
| +	vled->value = value;
 | |
| +
 | |
| +	if (vled == trig->active)
 | |
| +		led_trigger_event(&trig->trig, value);
 | |
| +}
 | |
| +
 | |
| +static int actpwr_brightness_set_blocking(struct led_classdev *led_cdev,
 | |
| +					  enum led_brightness value)
 | |
| +{
 | |
| +	actpwr_brightness_set(led_cdev, value);
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static enum led_brightness actpwr_brightness_get(struct led_classdev *led_cdev)
 | |
| +{
 | |
| +	struct actpwr_vled *vled = container_of(led_cdev, struct actpwr_vled,
 | |
| +					      cdev);
 | |
| +
 | |
| +	return vled->value;
 | |
| +}
 | |
| +
 | |
| +static void actpwr_trig_cycle(struct timer_list *t)
 | |
| +{
 | |
| +	struct actpwr_trig_data *trig  = &actpwr_data;
 | |
| +	struct actpwr_vled *active;
 | |
| +
 | |
| +	active = &trig->virt_leds[trig->next_active];
 | |
| +	trig->active = active;
 | |
| +	trig->next_active = (trig->next_active + 1) % TRIG_COUNT;
 | |
| +
 | |
| +	led_trigger_event(&trig->trig, active->value);
 | |
| +
 | |
| +	mod_timer(&trig->timer, jiffies + msecs_to_jiffies(active->interval));
 | |
| +}
 | |
| +
 | |
| +static int actpwr_trig_activate(struct led_classdev *led_cdev)
 | |
| +{
 | |
| +	struct actpwr_trig_data *trig  = &actpwr_data;
 | |
| +
 | |
| +	/* Start the timer if this is the first LED */
 | |
| +	if (!trig->active)
 | |
| +		actpwr_trig_cycle(&trig->timer);
 | |
| +	else
 | |
| +		led_set_brightness_nosleep(led_cdev, trig->active->value);
 | |
| +
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static void actpwr_trig_deactivate(struct led_classdev *led_cdev)
 | |
| +{
 | |
| +	struct actpwr_trig_data *trig  = &actpwr_data;
 | |
| +
 | |
| +	if (list_empty(&trig->trig.led_cdevs)) {
 | |
| +		del_timer_sync(&trig->timer);
 | |
| +		trig->active = NULL;
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +static int __init actpwr_trig_init(void)
 | |
| +{
 | |
| +	struct actpwr_trig_data *trig  = &actpwr_data;
 | |
| +	int ret = 0;
 | |
| +	int i;
 | |
| +
 | |
| +	timer_setup(&trig->timer, actpwr_trig_cycle, 0);
 | |
| +
 | |
| +	/* Register one "LED" for each source trigger */
 | |
| +	for (i = 0; i < TRIG_COUNT; i++)
 | |
| +	{
 | |
| +		struct actpwr_vled *vled = &trig->virt_leds[i];
 | |
| +		struct led_classdev *cdev = &vled->cdev;
 | |
| +		const struct actpwr_trig_src *src = &actpwr_trig_sources[i];
 | |
| +
 | |
| +		vled->parent = trig;
 | |
| +		vled->interval = src->interval;
 | |
| +		vled->invert = src->invert;
 | |
| +		cdev->name = src->name;
 | |
| +		cdev->brightness_set = actpwr_brightness_set;
 | |
| +		cdev->brightness_set_blocking = actpwr_brightness_set_blocking;
 | |
| +		cdev->brightness_get = actpwr_brightness_get;
 | |
| +		cdev->default_trigger = src->name;
 | |
| +		ret = led_classdev_register(NULL, cdev);
 | |
| +		if (ret)
 | |
| +			goto error_classdev;
 | |
| +	}
 | |
| +
 | |
| +	ret = led_trigger_register(&trig->trig);
 | |
| +	if (ret)
 | |
| +		goto error_classdev;
 | |
| +
 | |
| +	return 0;
 | |
| +
 | |
| +error_classdev:
 | |
| +	while (i > 0)
 | |
| +	{
 | |
| +		i--;
 | |
| +		led_classdev_unregister(&trig->virt_leds[i].cdev);
 | |
| +	}
 | |
| +
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +static void __exit actpwr_trig_exit(void)
 | |
| +{
 | |
| +	int i;
 | |
| +
 | |
| +	led_trigger_unregister(&actpwr_data.trig);
 | |
| +	for (i = 0; i < TRIG_COUNT; i++)
 | |
| +	{
 | |
| +		led_classdev_unregister(&actpwr_data.virt_leds[i].cdev);
 | |
| +	}
 | |
| +}
 | |
| +
 | |
| +module_init(actpwr_trig_init);
 | |
| +module_exit(actpwr_trig_exit);
 | |
| +
 | |
| +MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
 | |
| +MODULE_DESCRIPTION("ACT/PWR LED trigger");
 | |
| +MODULE_LICENSE("GPL v2");
 |