Add LED pattern helper
[led-pattern-ed] / src / led-pattern-helper.vala
1 /* This file is part of LED Pattern Editor.
2  *
3  * Copyright (C) 2010 Philipp Zabel
4  *
5  * LED Pattern Editor is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * LED Pattern Editor is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with LED Pattern Editor. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 public bool test_pattern (string pattern, bool apply) {
20         string[] key_value = pattern.split ("=");
21
22         if (key_value.length != 2) {
23                 stderr.printf ("pattern does not contain '=': %s\n", pattern);
24                 return false;
25         }
26
27         string[] p = key_value[1].split (";");
28         if (p.length != 6) {
29                 stderr.printf ("pattern does not contain 6 components: %d\n", p.length);
30                 return false;
31         }
32
33         if (p[0].has_prefix ("Pattern")) {
34                 stderr.printf ("pattern name doesn't start with 'Pattern': '%s'\n", p[0]);
35                 return false;
36         }
37
38         // priority = p[0].to_int ();
39         // screen_on = p[1].to_int ();
40         // timeout = p[2].to_int ();
41
42         int[] led_currents = { 2, 2, 2 };
43         string led_map1 = "000";
44         switch (p[3]) {
45         case "r":
46                 led_map1 = "001";
47                 led_currents = { 8, 0, 2 };
48                 break;
49         case "g":
50                 led_map1 = "010";
51                 led_currents = { 2, 2, 2 };
52                 break;
53         case "b":
54                 led_map1 = "100";
55                 led_currents = { 2, 2, 2 };
56                 break;
57         case "rg":
58                 led_map1 = "011";
59                 led_currents = { 20, 2, 0 };
60                 break;
61         case "rb":
62                 led_map1 = "101";
63                 // TODO: led_currents?
64                 break;
65         case "gb":
66                 led_map1 = "110";
67                 // TODO: led_currents?
68                 break;
69         case "rgb":
70                 led_map1 = "111";
71                 led_currents = { 8, 2, 2 };
72                 break;
73         default:
74                 stderr.printf ("only single-engine patterns supported for now\n");
75                 return false;
76         }
77
78         if (p[4].length > 16*4 || p[5].length > 16*4) {
79                 stderr.printf ("pattern too long!\n");
80                 return false;
81         }
82
83         if (!(p[4].has_prefix ("9d80"))) {
84                 stderr.printf ("engine1 pattern doesn't start with reset mux command\n");
85                 return false;
86         }
87
88         if (!(p[4].has_suffix ("0000")) && !(p[4].has_suffix ("c000"))) {
89                 stderr.printf ("engine1 pattern doesn't end with repeat or stop command\n");
90                 return false;
91         }
92
93         if (p[5] != "9d800000") {
94                 // FIXME
95                 stderr.printf ("only single-engine patterns supported for now\n");
96                 return false;
97         }
98
99         if (apply == false)
100                 return true;
101
102         string i2c_dev = "/sys/bus/i2c/devices/2-0032";
103
104         var f = FileStream.open ("/sys/class/leds/lp5523:r/led_current", "w");
105         if (f == null) {
106                 stderr.printf ("failed to set red led current\n");
107                 return false;
108         }
109         f.printf ("%d\n", led_currents[0]);
110
111         f = FileStream.open ("/sys/class/leds/lp5523:g/led_current", "w");
112         if (f == null) {
113                 stderr.printf ("failed to set green led current\n");
114                 return false;
115         }
116         f.printf ("%d\n", led_currents[1]);
117
118         f = FileStream.open ("/sys/class/leds/lp5523:b/led_current", "w");
119         if (f == null) {
120                 stderr.printf ("failed to set blue led current\n");
121                 return false;
122         }
123         f.printf ("%d\n", led_currents[2]);
124
125         f = FileStream.open (Path.build_filename (i2c_dev, "engine1_mode"), "w");
126         if (f == null) {
127                 stderr.printf ("failed to set engine1 to load mode\n");
128                 return false;
129         }
130         f.printf ("load\n");
131
132         int retries = 100;
133         for (int i = 0; i < retries; i++) {
134                 f = FileStream.open (Path.build_filename (i2c_dev, "engine1_leds"), "w");
135                 if (f != null)
136                         break;
137         }
138         if (f == null) {
139                 stderr.printf ("failed to set engine1 mux\n");
140                 return false;
141         }
142         f.printf ("0000%s00\n", led_map1);
143
144         f = FileStream.open (Path.build_filename (i2c_dev, "engine1_load"), "w");
145         if (f == null) {
146                 stderr.printf ("failed to load engine1 pattern\n");
147                 return false;
148         }
149         f.printf ("%s\n", p[4]);
150
151         f = FileStream.open (Path.build_filename (i2c_dev, "engine1_mode"), "w");
152         if (f == null) {
153                 stderr.printf ("failed to set engine1 to run mode\n");
154                 return false;
155         }
156         f.printf ("run\n");
157
158         return true;
159 }
160
161 bool mce_ini_check (string new_mce_ini) {
162         var f = FileStream.open ("/etc/mce/mce.ini", "r");
163         var g = FileStream.open (new_mce_ini, "r");
164
165         if (f == null || g == null) {
166                 stderr.printf ("failed to open /etc/mce/mce.ini and %s\n", new_mce_ini);
167                 return false;
168         }
169
170         var line1 = f.read_line ();
171         var line2 = g.read_line ();
172         while (line1 != null && line2 != null) {
173                 if (line1 == line2) {
174                         line1 = f.read_line ();
175                         line2 = g.read_line ();
176                         continue;
177                 }
178
179                 string[] key_value1 = line1.split ("=");
180                 string[] key_value2 = line2.split ("=");
181
182                 if (key_value1.length != 2 || key_value2.length != 2 || !line1.has_prefix ("Pattern")) {
183                         stderr.printf ("not allowed to change anything but led patterns:\n-%s\n+%s\n",
184                                        line1, line2);
185                         return false;
186                 }
187
188                 if (key_value1[0] != key_value2[0]) {
189                         stderr.printf ("not allowed to change pattern names\n-%s\n+%s\n",
190                                        line1, line2);
191                         return false;
192                 }
193
194                 if (!test_pattern (line2, false))
195                         return false;
196
197                 line1 = f.read_line ();
198                 line2 = g.read_line ();
199         }
200
201         if (line1 != line2) {
202                 stderr.printf ("different number of lines\n");
203                 return false;
204         }
205
206         return true;
207 }
208
209 int mce_copy (string source, string destination) {
210         var f = FileStream.open (source, "r");
211         var g = FileStream.open (destination, "w");
212
213         if (f == null || g == null) {
214                 return 1;
215         }
216
217         var line = f.read_line ();
218         while (line != null) {
219                 g.printf ("%s\n", line);
220                 line = f.read_line ();
221         }
222
223         return 0;
224 }
225
226 public static int main (string[] args) {
227         string usage = "usage: led-pattern-helper test <Pattern>\n";
228
229         if (args.length != 3) {
230                 stderr.printf (usage);
231                 return -1;
232         }
233
234         if (args[1] == "test") {
235                 if (!test_pattern (args[2], true))
236                         return -1;
237
238                 return 0;
239         }
240
241         if (args[1] == "save") {
242                 if (!FileUtils.test (args[2], FileTest.IS_REGULAR)) {
243                         stderr.printf ("not a regular file: %s\n", args[2]);
244                         return -1;
245                 }
246
247                 if (!mce_ini_check (args[2]))
248                         return -1;
249
250                 // Ok, we're good to go
251                 int result = mce_copy ("/etc/mce/mce.ini", "/etc/mce/mce.ini.led-pattern-old");
252                 if (result != 0) {
253                         stderr.printf ("failed to copy old mce.ini to mce.ini.led-pattern-old\n");
254                         return -1;
255                 }
256                 result = mce_copy (args[2], "/etc/mce/mce.ini.led-pattern-new");
257                 if (result != 0) {
258                         stderr.printf ("failed to copy new mce.ini to mce.ini.led-pattern-new\n");
259                         return -1;
260                 }
261                 result = FileUtils.rename ("/etc/mce/mce.ini.led-pattern-new", "/etc/mce/mce.ini");
262                 if (result != 0) {
263                         stderr.printf ("failed to replace MCE configuration: %d\n", result);
264                         return -1;
265                 }
266
267                 // Moment of truth, restart MCE
268                 try {
269                         int exit_status;
270                         string error;
271                         var command = "initctl stop mce";
272                         Process.spawn_command_line_sync (command, null, out error, out exit_status);
273                         if (exit_status != 0) {
274                                 stderr.printf ("stopping mce failed: %d\n%s", exit_status, error);
275                                 return -1;
276                         }
277                         command = "sleep 1";
278                         Process.spawn_command_line_sync (command, null, out error, out exit_status);
279                         command = "initctl start mce";
280                         Process.spawn_command_line_sync (command, null, out error, out exit_status);
281                         if (exit_status != 0) {
282                                 stderr.printf ("starting mce failed: %d\n%s", exit_status, error);
283                                 return -1;
284                         }
285                 } catch (SpawnError e) {
286                         stderr.printf ("restarting mce failed: %s", e.message);
287                         return -1;
288                 }
289
290                 return 0;
291         }
292
293         stderr.printf (usage);
294         return -1;
295 }