1 /*
2 * LED support code, ripped out of arch/arm/kernel/time.c
3 *
4 * Copyright (C) 1994-2001 Russell King
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10 #include <linux/export.h>
11 #include <linux/init.h>
12 #include <linux/device.h>
13 #include <linux/notifier.h>
14 #include <linux/cpu.h>
15 #include <linux/syscore_ops.h>
16 #include <linux/string.h>
17
18 #include <asm/leds.h>
19
dummy_leds_event(led_event_t evt)20 static void dummy_leds_event(led_event_t evt)
21 {
22 }
23
24 void (*leds_event)(led_event_t) = dummy_leds_event;
25
26 struct leds_evt_name {
27 const char name[8];
28 int on;
29 int off;
30 };
31
32 static const struct leds_evt_name evt_names[] = {
33 { "amber", led_amber_on, led_amber_off },
34 { "blue", led_blue_on, led_blue_off },
35 { "green", led_green_on, led_green_off },
36 { "red", led_red_on, led_red_off },
37 };
38
leds_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)39 static ssize_t leds_store(struct device *dev,
40 struct device_attribute *attr,
41 const char *buf, size_t size)
42 {
43 int ret = -EINVAL, len = strcspn(buf, " ");
44
45 if (len > 0 && buf[len] == '\0')
46 len--;
47
48 if (strncmp(buf, "claim", len) == 0) {
49 leds_event(led_claim);
50 ret = size;
51 } else if (strncmp(buf, "release", len) == 0) {
52 leds_event(led_release);
53 ret = size;
54 } else {
55 int i;
56
57 for (i = 0; i < ARRAY_SIZE(evt_names); i++) {
58 if (strlen(evt_names[i].name) != len ||
59 strncmp(buf, evt_names[i].name, len) != 0)
60 continue;
61 if (strncmp(buf+len, " on", 3) == 0) {
62 leds_event(evt_names[i].on);
63 ret = size;
64 } else if (strncmp(buf+len, " off", 4) == 0) {
65 leds_event(evt_names[i].off);
66 ret = size;
67 }
68 break;
69 }
70 }
71 return ret;
72 }
73
74 static DEVICE_ATTR(event, 0200, NULL, leds_store);
75
76 static struct bus_type leds_subsys = {
77 .name = "leds",
78 .dev_name = "leds",
79 };
80
81 static struct device leds_device = {
82 .id = 0,
83 .bus = &leds_subsys,
84 };
85
leds_suspend(void)86 static int leds_suspend(void)
87 {
88 leds_event(led_stop);
89 return 0;
90 }
91
leds_resume(void)92 static void leds_resume(void)
93 {
94 leds_event(led_start);
95 }
96
leds_shutdown(void)97 static void leds_shutdown(void)
98 {
99 leds_event(led_halted);
100 }
101
102 static struct syscore_ops leds_syscore_ops = {
103 .shutdown = leds_shutdown,
104 .suspend = leds_suspend,
105 .resume = leds_resume,
106 };
107
leds_idle_notifier(struct notifier_block * nb,unsigned long val,void * data)108 static int leds_idle_notifier(struct notifier_block *nb, unsigned long val,
109 void *data)
110 {
111 switch (val) {
112 case IDLE_START:
113 leds_event(led_idle_start);
114 break;
115 case IDLE_END:
116 leds_event(led_idle_end);
117 break;
118 }
119
120 return 0;
121 }
122
123 static struct notifier_block leds_idle_nb = {
124 .notifier_call = leds_idle_notifier,
125 };
126
leds_init(void)127 static int __init leds_init(void)
128 {
129 int ret;
130 ret = subsys_system_register(&leds_subsys, NULL);
131 if (ret == 0)
132 ret = device_register(&leds_device);
133 if (ret == 0)
134 ret = device_create_file(&leds_device, &dev_attr_event);
135 if (ret == 0) {
136 register_syscore_ops(&leds_syscore_ops);
137 idle_notifier_register(&leds_idle_nb);
138 }
139
140 return ret;
141 }
142
143 device_initcall(leds_init);
144
145 EXPORT_SYMBOL(leds_event);
146