7fc5ad53b9c5807ca99fd2e26c1734124efa5bc7
[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_map1 = 0x000;
43         int led_map2 = 0x000;
44         if ("r" in p[3])
45                 led_map1 |= 0x001;
46         if ("R" in p[3])
47                 led_map2 |= 0x001;
48         if ("g" in p[3])
49                 led_map1 |= 0x010;
50         if ("G" in p[3])
51                 led_map2 |= 0x010;
52         if ("b" in p[3])
53                 led_map1 |= 0x100;
54         if ("B" in p[3])
55                 led_map2 |= 0x100;
56
57         if ((led_map1 & led_map2) != 0) {
58                 stderr.printf ("pattern assigns a led to both channels: '%s'\n", p[3]);
59                 return false;
60         }
61
62         string led_map = "";
63         if (0x001 in led_map1)
64                 led_map += "r";
65         if (0x001 in led_map2)
66                 led_map += "R";
67         if (0x010 in led_map1)
68                 led_map += "g";
69         if (0x010 in led_map2)
70                 led_map += "G";
71         if (0x100 in led_map1)
72                 led_map += "b";
73         if (0x100 in led_map2)
74                 led_map += "B";
75
76         if (led_map != p[3]) {
77                 stderr.printf ("pattern contains invalid led map: '%s\n", p[3]);
78                 return false;
79         }
80
81         int[] led_currents = { 2, 2, 2 };
82         switch (led_map1 | led_map2) {
83         case 0x001:
84                 led_currents = { 8, 0, 2 };
85                 break;
86         case 0x010:
87                 led_currents = { 2, 2, 2 };
88                 break;
89         case 0x100:
90                 led_currents = { 2, 2, 2 };
91                 break;
92         case 0x011:
93                 led_currents = { 20, 2, 0 };
94                 break;
95         case 0x101:
96                 // TODO: led_currents?
97                 break;
98         case 0x110:
99                 // TODO: led_currents?
100                 break;
101         case 0x111:
102                 led_currents = { 8, 2, 2 };
103                 break;
104         }
105
106         if (p[4].length > 16*4 || p[5].length > 16*4) {
107                 stderr.printf ("engine1 pattern too long!\n");
108                 return false;
109         }
110
111         if (!(p[4].has_prefix ("9d80"))) {
112                 stderr.printf ("engine1 pattern doesn't start with reset mux command\n");
113                 return false;
114         }
115
116         if (!(p[4].has_suffix ("0000")) && !(p[4].has_suffix ("c000"))) {
117                 stderr.printf ("engine1 pattern doesn't end with repeat or stop command\n");
118                 return false;
119         }
120
121         if (p[5].length > 16*4 || p[5].length > 16*4) {
122                 stderr.printf ("engine2 pattern too long!\n");
123                 return false;
124         }
125
126         if (!(p[5].has_prefix ("9d80"))) {
127                 stderr.printf ("engine2 pattern doesn't start with reset mux command\n");
128                 return false;
129         }
130
131         if (!(p[5].has_suffix ("0000")) && !(p[5].has_suffix ("c000"))) {
132                 stderr.printf ("engine2 pattern doesn't end with repeat or stop command\n");
133                 return false;
134         }
135
136         if (apply == false)
137                 return true;
138
139         string i2c_dev = "/sys/bus/i2c/devices/2-0032";
140
141         var f = FileStream.open ("/sys/class/leds/lp5523:r/led_current", "w");
142         if (f == null) {
143                 stderr.printf ("failed to set red led current\n");
144                 return false;
145         }
146         f.printf ("%d\n", led_currents[0]);
147
148         f = FileStream.open ("/sys/class/leds/lp5523:g/led_current", "w");
149         if (f == null) {
150                 stderr.printf ("failed to set green led current\n");
151                 return false;
152         }
153         f.printf ("%d\n", led_currents[1]);
154
155         f = FileStream.open ("/sys/class/leds/lp5523:b/led_current", "w");
156         if (f == null) {
157                 stderr.printf ("failed to set blue led current\n");
158                 return false;
159         }
160         f.printf ("%d\n", led_currents[2]);
161
162         f = FileStream.open (Path.build_filename (i2c_dev, "engine1_mode"), "w");
163         if (f == null) {
164                 stderr.printf ("failed to set engine1 to load mode\n");
165                 return false;
166         }
167         f.printf ("load\n");
168
169         int retries = 100;
170         for (int i = 0; i < retries; i++) {
171                 f = FileStream.open (Path.build_filename (i2c_dev, "engine1_leds"), "w");
172                 if (f != null)
173                         break;
174         }
175         if (f == null) {
176                 stderr.printf ("failed to set engine1 mux\n");
177                 return false;
178         }
179         f.printf ("0000%03x00\n", led_map1);
180
181         f = FileStream.open (Path.build_filename (i2c_dev, "engine1_load"), "w");
182         if (f == null) {
183                 stderr.printf ("failed to load engine1 pattern\n");
184                 return false;
185         }
186         f.printf ("%s\n", p[4]);
187
188         if (led_map2 != 0x000) {
189                 f = FileStream.open (Path.build_filename (i2c_dev, "engine2_mode"), "w");
190                 if (f == null) {
191                         stderr.printf ("failed to set engine2 to load mode\n");
192                         return false;
193                 }
194                 f.printf ("load\n");
195
196                 for (int i = 0; i < retries; i++) {
197                         f = FileStream.open (Path.build_filename (i2c_dev, "engine2_leds"), "w");
198                         if (f != null)
199                                 break;
200                 }
201                 if (f == null) {
202                         stderr.printf ("failed to set engine2 mux\n");
203                         return false;
204                 }
205                 f.printf ("0000%03x00\n", led_map2);
206
207                 f = FileStream.open (Path.build_filename (i2c_dev, "engine2_load"), "w");
208                 if (f == null) {
209                         stderr.printf ("failed to load engine1 pattern\n");
210                         return false;
211                 }
212                 f.printf ("%s\n", p[5]);
213
214                 f = FileStream.open (Path.build_filename (i2c_dev, "engine2_mode"), "w");
215                 if (f == null) {
216                         stderr.printf ("failed to set engine2 to run mode\n");
217                         return false;
218                 }
219                 f.printf ("run\n");
220         }
221
222         f = FileStream.open (Path.build_filename (i2c_dev, "engine1_mode"), "w");
223         if (f == null) {
224                 stderr.printf ("failed to set engine1 to run mode\n");
225                 return false;
226         }
227         f.printf ("run\n");
228
229         return true;
230 }
231
232 bool mce_ini_check (string new_mce_ini) {
233         var f = FileStream.open ("/etc/mce/mce.ini", "r");
234         var g = FileStream.open (new_mce_ini, "r");
235
236         if (f == null || g == null) {
237                 stderr.printf ("failed to open /etc/mce/mce.ini and %s\n", new_mce_ini);
238                 return false;
239         }
240
241         var line1 = f.read_line ();
242         var line2 = g.read_line ();
243         while (line1 != null && line2 != null) {
244                 if (line1 == line2) {
245                         line1 = f.read_line ();
246                         line2 = g.read_line ();
247                         continue;
248                 }
249
250                 string[] key_value1 = line1.split ("=");
251                 string[] key_value2 = line2.split ("=");
252
253                 if (key_value1.length != 2 || key_value2.length != 2 || !line1.has_prefix ("Pattern")) {
254                         stderr.printf ("not allowed to change anything but led patterns:\n-%s\n+%s\n",
255                                        line1, line2);
256                         return false;
257                 }
258
259                 if (key_value1[0] != key_value2[0]) {
260                         stderr.printf ("not allowed to change pattern names\n-%s\n+%s\n",
261                                        line1, line2);
262                         return false;
263                 }
264
265                 if (!test_pattern (line2, false))
266                         return false;
267
268                 line1 = f.read_line ();
269                 line2 = g.read_line ();
270         }
271
272         if (line1 != line2) {
273                 stderr.printf ("different number of lines\n");
274                 return false;
275         }
276
277         return true;
278 }
279
280 int mce_copy (string source, string destination) {
281         var f = FileStream.open (source, "r");
282         var g = FileStream.open (destination, "w");
283
284         if (f == null || g == null) {
285                 return 1;
286         }
287
288         var line = f.read_line ();
289         while (line != null) {
290                 g.printf ("%s\n", line);
291                 line = f.read_line ();
292         }
293
294         return 0;
295 }
296
297 public static int main (string[] args) {
298         string usage = "usage: led-pattern-helper test <Pattern>\n";
299
300         if (args.length != 3) {
301                 stderr.printf (usage);
302                 return -1;
303         }
304
305         if (args[1] == "test") {
306                 if (!test_pattern (args[2], true))
307                         return -1;
308
309                 return 0;
310         }
311
312         if (args[1] == "save") {
313                 if (!FileUtils.test (args[2], FileTest.IS_REGULAR)) {
314                         stderr.printf ("not a regular file: %s\n", args[2]);
315                         return -1;
316                 }
317
318                 if (!mce_ini_check (args[2]))
319                         return -1;
320
321                 // Ok, we're good to go
322                 int result = mce_copy ("/etc/mce/mce.ini", "/etc/mce/mce.ini.led-pattern-old");
323                 if (result != 0) {
324                         stderr.printf ("failed to copy old mce.ini to mce.ini.led-pattern-old\n");
325                         return -1;
326                 }
327                 result = mce_copy (args[2], "/etc/mce/mce.ini.led-pattern-new");
328                 if (result != 0) {
329                         stderr.printf ("failed to copy new mce.ini to mce.ini.led-pattern-new\n");
330                         return -1;
331                 }
332                 result = FileUtils.rename ("/etc/mce/mce.ini.led-pattern-new", "/etc/mce/mce.ini");
333                 if (result != 0) {
334                         stderr.printf ("failed to replace MCE configuration: %d\n", result);
335                         return -1;
336                 }
337
338                 // Moment of truth, restart MCE
339                 try {
340                         int exit_status;
341                         string error;
342                         var command = "initctl stop mce";
343                         Process.spawn_command_line_sync (command, null, out error, out exit_status);
344                         if (exit_status != 0) {
345                                 stderr.printf ("stopping mce failed: %d\n%s", exit_status, error);
346                                 return -1;
347                         }
348                         command = "sleep 1";
349                         Process.spawn_command_line_sync (command, null, out error, out exit_status);
350                         command = "initctl start mce";
351                         Process.spawn_command_line_sync (command, null, out error, out exit_status);
352                         if (exit_status != 0) {
353                                 stderr.printf ("starting mce failed: %d\n%s", exit_status, error);
354                                 return -1;
355                         }
356                 } catch (SpawnError e) {
357                         stderr.printf ("restarting mce failed: %s", e.message);
358                         return -1;
359                 }
360
361                 return 0;
362         }
363
364         stderr.printf (usage);
365         return -1;
366 }