1/*
2 * LED ADM5120 Switch Port State Trigger
3 *
4 * Copyright (C) 2007 Bernhard Held <bernhard at bernhardheld.de>
5 * Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
6 *
7 * This file was based on: drivers/leds/ledtrig-timer.c
8 *	Copyright 2005-2006 Openedhand Ltd.
9 *	Author: Richard Purdie
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
14 *
15 */
16
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/init.h>
20#include <linux/device.h>
21
22#include <linux/gpio.h>
23
24#include "leds.h"
25
26#define DRV_NAME "port_state"
27#define DRV_DESC "LED ADM5120 Switch Port State Trigger"
28
29struct port_state {
30	char *name;
31	unsigned int value;
32};
33
34#define PORT_STATE(n, v) {.name = (n), .value = (v)}
35
36static struct port_state port_states[] = {
37	PORT_STATE("off",		LED_OFF),
38	PORT_STATE("on",		LED_FULL),
39	PORT_STATE("flash",		ADM5120_GPIO_FLASH),
40	PORT_STATE("link",		ADM5120_GPIO_LINK),
41	PORT_STATE("speed",		ADM5120_GPIO_SPEED),
42	PORT_STATE("duplex",		ADM5120_GPIO_DUPLEX),
43	PORT_STATE("act",		ADM5120_GPIO_ACT),
44	PORT_STATE("coll",		ADM5120_GPIO_COLL),
45	PORT_STATE("link_act",		ADM5120_GPIO_LINK_ACT),
46	PORT_STATE("duplex_coll",	ADM5120_GPIO_DUPLEX_COLL),
47	PORT_STATE("10M_act",		ADM5120_GPIO_10M_ACT),
48	PORT_STATE("100M_act",		ADM5120_GPIO_100M_ACT),
49};
50
51static ssize_t led_port_state_show(struct device *dev,
52		struct device_attribute *attr, char *buf)
53{
54	struct led_classdev *led_cdev = dev_get_drvdata(dev);
55	struct port_state *state = led_cdev->trigger_data;
56	int len = 0;
57	int i;
58
59	*buf = '\0';
60	for (i = 0; i < ARRAY_SIZE(port_states); i++) {
61		if (&port_states[i] == state)
62			len += sprintf(buf+len, "[%s] ", port_states[i].name);
63		else
64			len += sprintf(buf+len, "%s ", port_states[i].name);
65	}
66	len += sprintf(buf+len, "\n");
67
68	return len;
69}
70
71static ssize_t led_port_state_store(struct device *dev,
72		struct device_attribute *attr, const char *buf, size_t size)
73{
74	struct led_classdev *led_cdev = dev_get_drvdata(dev);
75	size_t len;
76	int i;
77
78	for (i = 0; i < ARRAY_SIZE(port_states); i++) {
79		len = strlen(port_states[i].name);
80		if (strncmp(port_states[i].name, buf, len) != 0)
81			continue;
82
83		if (buf[len] != '\0' && buf[len] != '\n')
84			continue;
85
86		led_cdev->trigger_data = &port_states[i];
87		led_set_brightness(led_cdev, port_states[i].value);
88		return size;
89	}
90
91	return -EINVAL;
92}
93
94static DEVICE_ATTR(port_state, 0644, led_port_state_show,
95			 led_port_state_store);
96
97static void adm5120_switch_trig_activate(struct led_classdev *led_cdev)
98{
99	struct port_state *state = port_states;
100	int rc;
101
102	led_cdev->trigger_data = state;
103
104	rc = device_create_file(led_cdev->dev, &dev_attr_port_state);
105	if (rc)
106		goto err;
107
108	led_set_brightness(led_cdev, state->value);
109
110	return;
111err:
112	led_cdev->trigger_data = NULL;
113}
114
115static void adm5120_switch_trig_deactivate(struct led_classdev *led_cdev)
116{
117	struct port_state *state = led_cdev->trigger_data;
118
119	if (!state)
120		return;
121
122	device_remove_file(led_cdev->dev, &dev_attr_port_state);
123
124}
125
126static struct led_trigger adm5120_switch_led_trigger = {
127	.name		= DRV_NAME,
128	.activate	= adm5120_switch_trig_activate,
129	.deactivate	= adm5120_switch_trig_deactivate,
130};
131
132static int __init adm5120_switch_trig_init(void)
133{
134	led_trigger_register(&adm5120_switch_led_trigger);
135	return 0;
136}
137
138static void __exit adm5120_switch_trig_exit(void)
139{
140	led_trigger_unregister(&adm5120_switch_led_trigger);
141}
142
143module_init(adm5120_switch_trig_init);
144module_exit(adm5120_switch_trig_exit);
145
146MODULE_AUTHOR("Bernhard Held <bernhard at bernhardheld.de>, "
147		"Gabor Juhos <juhosg@openwrt.org>");
148MODULE_DESCRIPTION(DRV_DESC);
149MODULE_LICENSE("GPL v2");
150